diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp index 710c7a76c..c6bd2d300 100644 --- a/js/src/builtin/ModuleObject.cpp +++ b/js/src/builtin/ModuleObject.cpp @@ -947,7 +947,7 @@ ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValu ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports) { MOZ_ASSERT(!self->namespace_()); - MOZ_ASSERT(exports->is() || exports->is()); + MOZ_ASSERT(exports->is()); RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self)); if (!ns) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c524184d6..7f9fa8a5d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -6281,8 +6281,8 @@ ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObje } MOZ_ASSERT(idx == count); - JSObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(), - newKind, arrayKind); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, values.begin(), values.length(), + newKind, arrayKind); if (!obj) return false; @@ -9192,7 +9192,7 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn) return false; } - if (!emitArray(args, argc, JSOP_SPREADCALLARRAY)) + if (!emitArray(args, argc)) return false; if (optCodeEmitted) { @@ -9683,11 +9683,11 @@ BytecodeEmitter::emitArrayLiteral(ParseNode* pn) } } - return emitArray(pn->pn_head, pn->pn_count, JSOP_NEWARRAY); + return emitArray(pn->pn_head, pn->pn_count); } bool -BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) +BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count) { /* @@ -9698,7 +9698,6 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) * to avoid dup'ing and popping the array as each element is added, as * JSOP_SETELEM/JSOP_SETPROP would do. */ - MOZ_ASSERT(op == JSOP_NEWARRAY || op == JSOP_SPREADCALLARRAY); uint32_t nspread = 0; for (ParseNode* elt = pn; elt; elt = elt->pn_next) { @@ -9719,7 +9718,7 @@ BytecodeEmitter::emitArray(ParseNode* pn, uint32_t count, JSOp op) // For arrays with spread, this is a very pessimistic allocation, the // minimum possible final size. - if (!emitUint32Operand(op, count - nspread)) // ARRAY + if (!emitUint32Operand(JSOP_NEWARRAY, count - nspread)) // ARRAY return false; ParseNode* pn2 = pn; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 814fa11d4..29050c846 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -521,7 +521,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitAtomOp(ParseNode* pn, JSOp op); MOZ_MUST_USE bool emitArrayLiteral(ParseNode* pn); - MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count, JSOp op); + MOZ_MUST_USE bool emitArray(ParseNode* pn, uint32_t count); MOZ_MUST_USE bool emitArrayComp(ParseNode* pn); MOZ_MUST_USE bool emitInternedScopeOp(uint32_t index, JSOp op); diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 3ea4c9d29..da3ef7d0d 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -18,6 +18,7 @@ #include "builtin/ModuleObject.h" #include "gc/GCInternals.h" #include "gc/Policy.h" +#include "gc/StoreBuffer-inl.h" #include "jit/IonCode.h" #include "js/SliceBudget.h" #include "vm/ArgumentsObject.h" @@ -28,7 +29,6 @@ #include "vm/Shape.h" #include "vm/Symbol.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" #include "wasm/WasmJS.h" #include "jscompartmentinlines.h" @@ -37,7 +37,6 @@ #include "gc/Nursery-inl.h" #include "vm/String-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::gc; @@ -1395,14 +1394,6 @@ js::ObjectGroup::traceChildren(JSTracer* trc) if (maybePreliminaryObjects()) maybePreliminaryObjects()->trace(trc); - if (maybeUnboxedLayout()) - unboxedLayout().trace(trc); - - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) { - TraceManuallyBarrieredEdge(trc, &unboxedGroup, "group_original_unboxed_group"); - setOriginalUnboxedGroup(unboxedGroup); - } - if (JSObject* descr = maybeTypeDescr()) { TraceManuallyBarrieredEdge(trc, &descr, "group_type_descr"); setTypeDescr(&descr->as()); @@ -1436,12 +1427,6 @@ js::GCMarker::lazilyMarkChildren(ObjectGroup* group) if (group->maybePreliminaryObjects()) group->maybePreliminaryObjects()->trace(this); - if (group->maybeUnboxedLayout()) - group->unboxedLayout().trace(this); - - if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup()) - traverseEdge(group, unboxedGroup); - if (TypeDescr* descr = group->maybeTypeDescr()) traverseEdge(group, static_cast(descr)); @@ -1484,23 +1469,6 @@ CallTraceHook(Functor f, JSTracer* trc, JSObject* obj, CheckGeneration check, Ar return nullptr; } - if (clasp == &UnboxedPlainObject::class_) { - JSObject** pexpando = obj->as().addressOfExpando(); - if (*pexpando) - f(pexpando, mozilla::Forward(args)...); - - UnboxedPlainObject& unboxed = obj->as(); - const UnboxedLayout& layout = check == CheckGeneration::DoChecks - ? unboxed.layout() - : unboxed.layoutDontCheckGeneration(); - if (layout.traceList()) { - VisitTraceList(f, layout.traceList(), unboxed.data(), - mozilla::Forward(args)...); - } - - return nullptr; - } - clasp->doTrace(trc, obj); if (!clasp->isNative()) @@ -2293,18 +2261,6 @@ static inline void TraceWholeCell(TenuringTracer& mover, JSObject* object) { mover.traceObject(object); - - // Additionally trace the expando object attached to any unboxed plain - // objects. Baseline and Ion can write properties to the expando while - // only adding a post barrier to the owning unboxed object. Note that - // it isn't possible for a nursery unboxed object to have a tenured - // expando, so that adding a post barrier on the original object will - // capture any tenured->nursery edges in the expando as well. - - if (object->is()) { - if (UnboxedExpandoObject* expando = object->as().maybeExpando()) - expando->traceChildren(&mover); - } } static inline void @@ -2548,8 +2504,6 @@ js::TenuringTracer::moveObjectToTenured(JSObject* dst, JSObject* src, AllocKind InlineTypedObject::objectMovedDuringMinorGC(this, dst, src); } else if (src->is()) { tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind); - } else if (src->is()) { - tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind); } else if (src->is()) { tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src); } else if (src->is()) { diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index 63cd9b08a..3416464dd 100644 --- a/js/src/gc/Tracer.cpp +++ b/js/src/gc/Tracer.cpp @@ -201,80 +201,15 @@ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape) } while (shape); } -// Object groups can point to other object groups via an UnboxedLayout or the -// the original unboxed group link. There can potentially be deep or cyclic -// chains of such groups to trace through without going through a thing that -// participates in cycle collection. These need to be handled iteratively to -// avoid blowing the stack when running the cycle collector's callback tracer. -struct ObjectGroupCycleCollectorTracer : public JS::CallbackTracer -{ - explicit ObjectGroupCycleCollectorTracer(JS::CallbackTracer* innerTracer) - : JS::CallbackTracer(innerTracer->runtime(), DoNotTraceWeakMaps), - innerTracer(innerTracer) - {} - - void onChild(const JS::GCCellPtr& thing) override; - - JS::CallbackTracer* innerTracer; - Vector seen, worklist; -}; - -void -ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing) -{ - if (thing.is()) { - // The CC does not care about BaseShapes, and no additional GC things - // will be reached by following this edge. - return; - } - - if (thing.is() || thing.is()) { - // Invoke the inner cycle collector callback on this child. It will not - // recurse back into TraceChildren. - innerTracer->onChild(thing); - return; - } - - if (thing.is()) { - // If this group is required to be in an ObjectGroup chain, trace it - // via the provided worklist rather than continuing to recurse. - ObjectGroup& group = thing.as(); - if (group.maybeUnboxedLayout()) { - for (size_t i = 0; i < seen.length(); i++) { - if (seen[i] == &group) - return; - } - if (seen.append(&group) && worklist.append(&group)) { - return; - } else { - // If append fails, keep tracing normally. The worst that will - // happen is we end up overrecursing. - } - } - } - - TraceChildren(this, thing.asCell(), thing.kind()); -} - void gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group) { MOZ_ASSERT(trc->isCallbackTracer()); - // Early return if this group is not required to be in an ObjectGroup chain. - if (!group->maybeUnboxedLayout()) - return group->traceChildren(trc); - - ObjectGroupCycleCollectorTracer groupTracer(trc->asCallbackTracer()); - group->traceChildren(&groupTracer); - - while (!groupTracer.worklist.empty()) { - ObjectGroup* innerGroup = groupTracer.worklist.popCopy(); - innerGroup->traceChildren(&groupTracer); - } + group->traceChildren(trc); } - + /*** Traced Edge Printer *************************************************************************/ static size_t diff --git a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js b/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js deleted file mode 100644 index f55456222..000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-clear-new-script.js +++ /dev/null @@ -1,49 +0,0 @@ - -function Foo(a, b) { - this.a = a; - this.b = b; -} - -function invalidate_foo() { - var a = []; - var counter = 0; - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - Object.defineProperty(Foo.prototype, "a", {configurable: true, set: function() { counter++; }}); - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - delete Foo.prototype.a; - var total = 0; - for (var i = 0; i < a.length; i++) { - assertEq('a' in a[i], i < 50); - total += a[i].b; - } - assertEq(total, 2550); - assertEq(counter, 50); -} -invalidate_foo(); - -function Bar(a, b, fn) { - this.a = a; - if (b == 30) - Object.defineProperty(Bar.prototype, "b", {configurable: true, set: fn}); - this.b = b; -} - -function invalidate_bar() { - var a = []; - var counter = 0; - function fn() { counter++; } - for (var i = 0; i < 50; i++) - a.push(new Bar(i, i + 1, fn)); - delete Bar.prototype.b; - var total = 0; - for (var i = 0; i < a.length; i++) { - assertEq('a' in a[i], true); - assertEq('b' in a[i], i < 29); - total += a[i].a; - } - assertEq(total, 1225); - assertEq(counter, 21); -} -invalidate_bar(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js b/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js deleted file mode 100644 index 691fe166c..000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-convert-to-native.js +++ /dev/null @@ -1,47 +0,0 @@ - -// Test various ways of converting an unboxed object to native. - -function Foo(a, b) { - this.a = a; - this.b = b; -} - -var proxyObj = { - get: function(recipient, name) { - return recipient[name] + 2; - } -}; - -function f() { - var a = []; - for (var i = 0; i < 50; i++) - a.push(new Foo(i, i + 1)); - - var prop = "a"; - - i = 0; - for (; i < 5; i++) - a[i].c = i; - for (; i < 10; i++) - Object.defineProperty(a[i], 'c', {value: i}); - for (; i < 15; i++) - a[i] = new Proxy(a[i], proxyObj); - for (; i < 20; i++) - a[i].a = 3.5; - for (; i < 25; i++) - delete a[i].b; - for (; i < 30; i++) - a[prop] = 4; - - var total = 0; - for (i = 0; i < a.length; i++) { - if ('a' in a[i]) - total += a[i].a; - if ('b' in a[i]) - total += a[i].b; - if ('c' in a[i]) - total += a[i].c; - } - assertEq(total, 2382.5); -} -f(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-getelem.js b/js/src/jit-test/tests/basic/unboxed-object-getelem.js deleted file mode 100644 index b30b8325a..000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-getelem.js +++ /dev/null @@ -1,20 +0,0 @@ - -function f() { - var propNames = ["a","b","c","d","e","f","g","h","i","j","x","y"]; - var arr = []; - for (var i=0; i<64; i++) - arr.push({x:1, y:2}); - for (var i=0; i<64; i++) { - // Make sure there are expandos with dynamic slots for each object. - for (var j = 0; j < propNames.length; j++) - arr[i][propNames[j]] = j; - } - var res = 0; - for (var i=0; i<100000; i++) { - var o = arr[i % 64]; - var p = propNames[i % propNames.length]; - res += o[p]; - } - assertEq(res, 549984); -} -f(); diff --git a/js/src/jit-test/tests/basic/unboxed-object-set-property.js b/js/src/jit-test/tests/basic/unboxed-object-set-property.js deleted file mode 100644 index fdcfcf6b2..000000000 --- a/js/src/jit-test/tests/basic/unboxed-object-set-property.js +++ /dev/null @@ -1,31 +0,0 @@ - -// Use the correct receiver when non-native objects are prototypes of other objects. - -function Thing(a, b) { - this.a = a; - this.b = b; -} - -function foo() { - var array = []; - for (var i = 0; i < 10000; i++) - array.push(new Thing(i, i + 1)); - - var proto = new Thing(1, 2); - var obj = Object.create(proto); - - Object.defineProperty(Thing.prototype, "c", {set:function() { this.d = 0; }}); - obj.c = 3; - assertEq(obj.c, undefined); - assertEq(obj.d, 0); - assertEq(obj.hasOwnProperty("d"), true); - assertEq(proto.d, undefined); - assertEq(proto.hasOwnProperty("d"), false); - - obj.a = 3; - assertEq(obj.a, 3); - assertEq(proto.a, 1); - assertEq(obj.hasOwnProperty("a"), true); -} - -foo(); diff --git a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js deleted file mode 100644 index 142d932dd..000000000 --- a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js +++ /dev/null @@ -1,24 +0,0 @@ -function O() { - this.x = 1; - this.y = 2; -} -function testUnboxed() { - var arr = []; - for (var i=0; i<100; i++) - arr.push(new O); - - var o = arr[arr.length-1]; - o[0] = 0; - o[2] = 2; - var sym = Symbol(); - o[sym] = 1; - o.z = 3; - Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false}); - o[4] = 4; - - var props = Reflect.ownKeys(o); - assertEq(props[props.length-1], sym); - - assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz"); -} -testUnboxed(); diff --git a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js b/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js deleted file mode 100644 index 02e27614f..000000000 --- a/js/src/jit-test/tests/ion/unboxed-objects-invalidate.js +++ /dev/null @@ -1,16 +0,0 @@ - -var a = []; -for (var i = 0; i < 2000; i++) - a.push({f:i}); - -function f() { - var total = 0; - for (var i = 0; i < a.length; i++) - total += a[i].f; - return total; -} -assertEq(f(), 1999000); - -var sub = Object.create(a[0]); - -assertEq(f(), 1999000); diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index 81c0fd067..0f0d4a66a 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -93,10 +93,6 @@ GetObject(const MDefinition* ins) case MDefinition::Op_Elements: case MDefinition::Op_MaybeCopyElementsForWrite: case MDefinition::Op_MaybeToDoubleElement: - case MDefinition::Op_UnboxedArrayLength: - case MDefinition::Op_UnboxedArrayInitializedLength: - case MDefinition::Op_IncrementUnboxedArrayInitializedLength: - case MDefinition::Op_SetUnboxedArrayInitializedLength: case MDefinition::Op_TypedArrayLength: case MDefinition::Op_SetTypedObjectOffset: case MDefinition::Op_SetDisjointTypedElements: @@ -114,8 +110,6 @@ GetObject(const MDefinition* ins) case MDefinition::Op_GuardObjectGroup: case MDefinition::Op_GuardObjectIdentity: case MDefinition::Op_GuardClass: - case MDefinition::Op_GuardUnboxedExpando: - case MDefinition::Op_LoadUnboxedExpando: case MDefinition::Op_LoadSlot: case MDefinition::Op_StoreSlot: case MDefinition::Op_InArray: diff --git a/js/src/jit/BaselineCacheIR.cpp b/js/src/jit/BaselineCacheIR.cpp index bf96932d1..67c80473b 100644 --- a/js/src/jit/BaselineCacheIR.cpp +++ b/js/src/jit/BaselineCacheIR.cpp @@ -16,7 +16,7 @@ using namespace js; using namespace js::jit; // OperandLocation represents the location of an OperandId. The operand is -// either in a register or on the stack, and is either boxed or unboxed. +// either in a register or on the stack. class OperandLocation { public: @@ -787,9 +787,6 @@ BaselineCacheIRCompiler::emitGuardClass() case GuardClassKind::Array: clasp = &ArrayObject::class_; break; - case GuardClassKind::UnboxedArray: - clasp = &UnboxedArrayObject::class_; - break; case GuardClassKind::MappedArguments: clasp = &MappedArgumentsObject::class_; break; @@ -817,36 +814,6 @@ BaselineCacheIRCompiler::emitGuardSpecificObject() return true; } -bool -BaselineCacheIRCompiler::emitGuardNoUnboxedExpando() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - - FailurePath* failure; - if (!addFailurePath(&failure)) - return false; - - Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::NotEqual, expandoAddr, ImmWord(0), failure->label()); - return true; -} - -bool -BaselineCacheIRCompiler::emitGuardAndLoadUnboxedExpando() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - Register output = allocator.defineRegister(masm, reader.objOperandId()); - - FailurePath* failure; - if (!addFailurePath(&failure)) - return false; - - Address expandoAddr(obj, UnboxedPlainObject::offsetOfExpando()); - masm.loadPtr(expandoAddr, output); - masm.branchTestPtr(Assembler::Zero, output, output, failure->label()); - return true; -} - bool BaselineCacheIRCompiler::emitLoadFixedSlotResult() { @@ -873,26 +840,6 @@ BaselineCacheIRCompiler::emitLoadDynamicSlotResult() return true; } -bool -BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - AutoScratchRegister scratch(allocator, masm); - - JSValueType fieldType = reader.valueType(); - - Address fieldOffset(stubAddress(reader.stubOffset())); - masm.load32(fieldOffset, scratch); - masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0); - - if (fieldType == JSVAL_TYPE_OBJECT) - emitEnterTypeMonitorIC(); - else - emitReturnFromIC(); - - return true; -} - bool BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects() { @@ -1003,19 +950,6 @@ BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult() return true; } -bool -BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult() -{ - Register obj = allocator.useRegister(masm, reader.objOperandId()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg()); - masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); - - // The int32 type was monitored when attaching the stub, so we can - // just return. - emitReturnFromIC(); - return true; -} - bool BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult() { diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 3fa5a80ed..6b64bfb44 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -2049,13 +2049,7 @@ BaselineCompiler::emit_JSOP_NEWARRAY() return true; } -bool -BaselineCompiler::emit_JSOP_SPREADCALLARRAY() -{ - return emit_JSOP_NEWARRAY(); -} - -typedef JSObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); +typedef ArrayObject* (*NewArrayCopyOnWriteFn)(JSContext*, HandleArrayObject, gc::InitialHeap); const VMFunction jit::NewArrayCopyOnWriteInfo = FunctionInfo(js::NewDenseCopyOnWriteArray, "NewDenseCopyOnWriteArray"); @@ -4136,14 +4130,14 @@ BaselineCompiler::emit_JSOP_REST() { frame.syncStack(0); - JSObject* templateObject = + ArrayObject* templateObject = ObjectGroup::newArrayObject(cx, nullptr, 0, TenuredObject, ObjectGroup::NewArrayKind::UnknownIndex); if (!templateObject) return false; // Call IC. - ICRest_Fallback::Compiler compiler(cx, &templateObject->as()); + ICRest_Fallback::Compiler compiler(cx, templateObject); if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 6b5bf009e..910a52980 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -100,7 +100,6 @@ namespace jit { _(JSOP_BITNOT) \ _(JSOP_NEG) \ _(JSOP_NEWARRAY) \ - _(JSOP_SPREADCALLARRAY) \ _(JSOP_NEWARRAY_COPYONWRITE) \ _(JSOP_INITELEM_ARRAY) \ _(JSOP_NEWOBJECT) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 863c61161..d91958d50 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -42,8 +42,8 @@ #include "jit/shared/Lowering-shared-inl.h" #include "vm/EnvironmentObject-inl.h" #include "vm/Interpreter-inl.h" +#include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" -#include "vm/UnboxedObject-inl.h" using mozilla::DebugOnly; @@ -289,7 +289,7 @@ DoTypeUpdateFallback(JSContext* cx, BaselineFrame* frame, ICUpdatedStub* stub, H case ICStub::SetProp_Native: case ICStub::SetProp_NativeAdd: case ICStub::SetProp_Unboxed: { - MOZ_ASSERT(obj->isNative() || obj->is()); + MOZ_ASSERT(obj->isNative()); jsbytecode* pc = stub->getChainFallback()->icEntry()->pc(script); if (*pc == JSOP_SETALIASEDVAR || *pc == JSOP_INITALIASEDLEXICAL) id = NameToId(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc)); @@ -732,11 +732,6 @@ LastPropertyForSetProp(JSObject* obj) if (obj->isNative()) return obj->as().lastProperty(); - if (obj->is()) { - UnboxedExpandoObject* expando = obj->as().maybeExpando(); - return expando ? expando->lastProperty() : nullptr; - } - return nullptr; } @@ -1153,56 +1148,6 @@ TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsb ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub(); - if (obj->is() && holder == obj) { - const UnboxedLayout::Property* property = obj->as().layout().lookup(id); - - // Once unboxed objects support symbol-keys, we need to change the following accordingly - MOZ_ASSERT_IF(!keyVal.isString(), !property); - - if (property) { - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); - ICGetElemNativeCompiler compiler(cx, ICStub::GetElem_UnboxedPropertyName, - monitorStub, obj, holder, - name, - ICGetElemNativeStub::UnboxedProperty, - needsAtomize, property->offset + - UnboxedPlainObject::offsetOfData(), - property->type); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; - } - - Shape* shape = obj->as().maybeExpando()->lookup(cx, id); - if (!shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - bool isFixedSlot; - uint32_t offset; - GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset); - - ICGetElemNativeStub::AccessType acctype = - isFixedSlot ? ICGetElemNativeStub::FixedSlot - : ICGetElemNativeStub::DynamicSlot; - ICGetElemNativeCompiler compiler(cx, getGetElemStubKind(ICStub::GetElem_NativeSlotName), - monitorStub, obj, holder, key, - acctype, needsAtomize, offset); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; - return true; - } - if (!holder->isNative()) return true; @@ -1366,7 +1311,7 @@ IsNativeDenseElementAccess(HandleObject obj, HandleValue key) static bool IsNativeOrUnboxedDenseElementAccess(HandleObject obj, HandleValue key) { - if (!obj->isNative() && !obj->is()) + if (!obj->isNative()) return false; if (key.isInt32() && key.toInt32() >= 0 && !obj->is()) return true; @@ -1450,7 +1395,7 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ } // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses. - if (obj->isNative() || obj->is()) { + if (obj->isNative()) { RootedScript rootedScript(cx, script); if (rhs.isString()) { if (!TryAttachNativeOrUnboxedGetValueElemStub(cx, rootedScript, pc, stub, @@ -1470,20 +1415,6 @@ TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_ script = rootedScript; } - // Check for UnboxedArray[int] accesses. - if (obj->is() && rhs.isInt32() && rhs.toInt32() >= 0) { - JitSpew(JitSpew_BaselineIC, " Generating GetElem(UnboxedArray[Int32]) stub"); - ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - obj->group()); - ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script)); - if (!unboxedStub) - return false; - - stub->addNewStub(unboxedStub); - *attached = true; - return true; - } - // Check for TypedArray[int] => Number and TypedObject[int] => Number accesses. if ((obj->is() || IsPrimitiveArrayTypedObject(obj)) && rhs.isNumber() && @@ -1876,14 +1807,6 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) Register holderReg; if (obj_ == holder_) { holderReg = objReg; - - if (obj_->is() && acctype_ != ICGetElemNativeStub::UnboxedProperty) { - // The property will be loaded off the unboxed expando. - masm.push(R1.scratchReg()); - popR1 = true; - holderReg = R1.scratchReg(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - } } else { // Shape guard holder. if (regs.empty()) { @@ -1934,13 +1857,6 @@ ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm) if (popR1) masm.addToStackPtr(ImmWord(sizeof(size_t))); - } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) { - masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()), - scratchReg); - masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_, - TypedOrValueRegister(R0)); - if (popR1) - masm.addToStackPtr(ImmWord(sizeof(size_t))); } else { MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter || acctype_ == ICGetElemNativeStub::ScriptedGetter); @@ -2089,56 +2005,6 @@ ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm) return true; } -// -// GetElem_UnboxedArray -// - -bool -ICGetElem_UnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - masm.branchTestInt32(Assembler::NotEqual, R1, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratchReg = regs.takeAny(); - - // Unbox R0 and group guard. - Register obj = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICGetElem_UnboxedArray::offsetOfGroup()), scratchReg); - masm.branchTestObjGroup(Assembler::NotEqual, obj, scratchReg, &failure); - - // Unbox key. - Register key = masm.extractInt32(R1, ExtractTemp1); - - // Bounds check. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), - scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Load value. - size_t width = UnboxedTypeSize(elementType_); - BaseIndex addr(scratchReg, key, ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, elementType_, R0); - - // Only monitor the result if its type might change. - if (elementType_ == JSVAL_TYPE_OBJECT) - EmitEnterTypeMonitorIC(masm); - else - EmitReturnFromIC(masm); - - // Failure case - jump to next stub - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // GetElem_TypedArray // @@ -2437,8 +2303,8 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength, bool* isAddingCaseOut, size_t* protoDepthOut) { - uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj); - uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj); + uint32_t initLength = obj->as().getDenseInitializedLength(); + uint32_t capacity = obj->as().getDenseCapacity(); *isAddingCaseOut = false; *protoDepthOut = 0; @@ -2447,10 +2313,6 @@ CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index, if (initLength < oldInitLength || capacity < oldCapacity) return false; - // Unboxed arrays need to be able to emit floating point code. - if (obj->is() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint) - return false; - Shape* shape = obj->maybeShape(); // Cannot optimize if the shape changed. @@ -2532,8 +2394,8 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_ uint32_t oldCapacity = 0; uint32_t oldInitLength = 0; if (index.isInt32() && index.toInt32() >= 0) { - oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj); - oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj); + oldCapacity = obj->as().getDenseCapacity(); + oldInitLength = obj->as().getDenseInitializedLength(); } if (op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM) { @@ -2741,18 +2603,6 @@ BaselineScript::noteArrayWriteHole(uint32_t pcOffset) // SetElem_DenseOrUnboxedArray // -template -void -EmitUnboxedPreBarrierForBaseline(MacroAssembler &masm, T address, JSValueType type) -{ - if (type == JSVAL_TYPE_OBJECT) - EmitPreBarrier(masm, address, MIRType::Object); - else if (type == JSVAL_TYPE_STRING) - EmitPreBarrier(masm, address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); -} - bool ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) { @@ -2871,29 +2721,6 @@ ICSetElem_DenseOrUnboxedArray::Compiler::generateStubCode(MacroAssembler& masm) masm.loadValue(valueAddr, tmpVal); EmitPreBarrier(masm, element, MIRType::Value); masm.storeValue(tmpVal, element); - } else { - // Set element on an unboxed array. - - // Bounds check. - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Compute the address being written to. - BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - - EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_); - - Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); - masm.Push(R0); - masm.loadValue(valueAddr, R0); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); } EmitReturnFromIC(masm); @@ -3087,40 +2914,6 @@ ICSetElemDenseOrUnboxedArrayAddCompiler::generateStubCode(MacroAssembler& masm) BaseIndex element(scratchReg, key, TimesEight); masm.loadValue(valueAddr, tmpVal); masm.storeValue(tmpVal, element); - } else { - // Adding element to an unboxed array. - - // Bounds check (key == initLength) - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLengthAddr, scratchReg); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg); - masm.branch32(Assembler::NotEqual, scratchReg, key, &failure); - - // Capacity check. - masm.checkUnboxedArrayCapacity(obj, RegisterOrInt32Constant(key), scratchReg, &failure); - - // Load obj->elements. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg); - - // Write the value first, since this can fail. No need for pre-barrier - // since we're not overwriting an old value. - masm.Push(R0); - Address valueAddr(masm.getStackPointer(), ICStackValueOffset + sizeof(Value)); - masm.loadValue(valueAddr, R0); - BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_))); - masm.storeUnboxedProperty(address, unboxedType_, - ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0); - masm.Pop(R0); - - // Increment initialized length. - masm.add32(Imm32(1), initLengthAddr); - - // If length is now <= key, increment length. - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - Label skipIncrementLength; - masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&skipIncrementLength); } EmitReturnFromIC(masm); @@ -4256,18 +4049,7 @@ TryAttachSetValuePropStub(JSContext* cx, HandleScript script, jsbytecode* pc, IC return true; if (!obj->isNative()) { - if (obj->is()) { - UnboxedExpandoObject* expando = obj->as().maybeExpando(); - if (expando) { - shape = expando->lookup(cx, name); - if (!shape) - return true; - } else { - return true; - } - } else { - return true; - } + return true; } size_t chainDepth; @@ -4417,40 +4199,6 @@ TryAttachSetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecode* pc, return true; } -static bool -TryAttachUnboxedSetPropStub(JSContext* cx, HandleScript script, - ICSetProp_Fallback* stub, HandleId id, - HandleObject obj, HandleValue rhs, bool* attached) -{ - MOZ_ASSERT(!*attached); - - if (!cx->runtime()->jitSupportsFloatingPoint) - return true; - - if (!obj->is()) - return true; - - const UnboxedLayout::Property* property = obj->as().layout().lookup(id); - if (!property) - return true; - - ICSetProp_Unboxed::Compiler compiler(cx, obj->group(), - property->offset + UnboxedPlainObject::offsetOfData(), - property->type); - ICUpdatedStub* newStub = compiler.getStub(compiler.getStubSpace(script)); - if (!newStub) - return false; - if (compiler.needsUpdateStubs() && !newStub->addUpdateStubForValue(cx, script, obj, id, rhs)) - return false; - - stub->addNewStub(newStub); - - StripPreliminaryObjectStubs(cx, stub); - - *attached = true; - return true; -} - static bool TryAttachTypedObjectSetPropStub(JSContext* cx, HandleScript script, ICSetProp_Fallback* stub, HandleId id, @@ -4534,12 +4282,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ return false; RootedReceiverGuard oldGuard(cx, ReceiverGuard(obj)); - if (obj->is()) { - MOZ_ASSERT(!oldShape); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - oldShape = expando->lastProperty(); - } - bool attached = false; // There are some reasons we can fail to attach a stub that are temporary. // We want to avoid calling noteUnoptimizableAccess() if the reason we @@ -4610,15 +4352,6 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ if (attached) return true; - if (!attached && - lhs.isObject() && - !TryAttachUnboxedSetPropStub(cx, script, stub, id, obj, rhs, &attached)) - { - return false; - } - if (attached) - return true; - if (!attached && lhs.isObject() && !TryAttachTypedObjectSetPropStub(cx, script, stub, id, obj, rhs, &attached)) @@ -4703,20 +4436,7 @@ GuardGroupAndShapeMaybeUnboxedExpando(MacroAssembler& masm, JSObject* obj, // Guard against shape or expando shape. masm.loadPtr(Address(ICStubReg, offsetOfShape), scratch); - if (obj->is()) { - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); } bool @@ -4755,13 +4475,7 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler& masm) regs.takeUnchecked(objReg); Register holderReg; - if (obj_->is()) { - // We are loading off the expando object, so use that for the holder. - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); - if (!isFixedSlot_) - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); - } else if (isFixedSlot_) { + if (isFixedSlot_) { holderReg = objReg; } else { holderReg = regs.takeAny(); @@ -4898,31 +4612,17 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) regs.add(R0); regs.takeUnchecked(objReg); - if (obj_->is()) { - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg); + // Write the object's new shape. + Address shapeAddr(objReg, ShapedObject::offsetOfShape()); + EmitPreBarrier(masm, shapeAddr, MIRType::Shape); + masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); + masm.storePtr(scratch, shapeAddr); - // Write the expando object's new shape. - Address shapeAddr(holderReg, ShapedObject::offsetOfShape()); - EmitPreBarrier(masm, shapeAddr, MIRType::Shape); - masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); - masm.storePtr(scratch, shapeAddr); - - if (!isFixedSlot_) - masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg); + if (isFixedSlot_) { + holderReg = objReg; } else { - // Write the object's new shape. - Address shapeAddr(objReg, ShapedObject::offsetOfShape()); - EmitPreBarrier(masm, shapeAddr, MIRType::Shape); - masm.loadPtr(Address(ICStubReg, ICSetProp_NativeAdd::offsetOfNewShape()), scratch); - masm.storePtr(scratch, shapeAddr); - - if (isFixedSlot_) { - holderReg = objReg; - } else { - holderReg = regs.takeAny(); - masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); - } + holderReg = regs.takeAny(); + masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), holderReg); } // Perform the store. No write barrier required since this is a new @@ -4953,70 +4653,6 @@ ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler& masm) return true; } -bool -ICSetProp_Unboxed::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - // Guard input is an object. - masm.branchTestObject(Assembler::NotEqual, R0, &failure); - - AllocatableGeneralRegisterSet regs(availableGeneralRegs(2)); - Register scratch = regs.takeAny(); - - // Unbox and group guard. - Register object = masm.extractObject(R0, ExtractTemp0); - masm.loadPtr(Address(ICStubReg, ICSetProp_Unboxed::offsetOfGroup()), scratch); - masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), scratch, - &failure); - - if (needsUpdateStubs()) { - // Stow both R0 and R1 (object and value). - EmitStowICValues(masm, 2); - - // Move RHS into R0 for TypeUpdate check. - masm.moveValue(R1, R0); - - // Call the type update stub. - if (!callTypeUpdateIC(masm, sizeof(Value))) - return false; - - // Unstow R0 and R1 (object and key) - EmitUnstowICValues(masm, 2); - - // The TypeUpdate IC may have smashed object. Rederive it. - masm.unboxObject(R0, object); - - // Trigger post barriers here on the values being written. Fields which - // objects can be written to also need update stubs. - LiveGeneralRegisterSet saveRegs; - saveRegs.add(R0); - saveRegs.add(R1); - saveRegs.addUnchecked(object); - saveRegs.add(ICStubReg); - emitPostWriteBarrierSlot(masm, object, R1, scratch, saveRegs); - } - - // Compute the address being written to. - masm.load32(Address(ICStubReg, ICSetProp_Unboxed::offsetOfFieldOffset()), scratch); - BaseIndex address(object, scratch, TimesOne); - - EmitUnboxedPreBarrierForBaseline(masm, address, fieldType_); - masm.storeUnboxedProperty(address, fieldType_, - ConstantOrRegister(TypedOrValueRegister(R1)), &failure); - - // The RHS has to be in R0. - masm.moveValue(R1, R0); - - EmitReturnFromIC(masm); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - bool ICSetProp_TypedObject::Compiler::generateStubCode(MacroAssembler& masm) { @@ -5490,13 +5126,6 @@ GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject return true; } -static void -EnsureArrayGroupAnalyzed(JSContext* cx, JSObject* obj) -{ - if (PreliminaryObjectArrayWithTemplate* objects = obj->group()->maybePreliminaryObjects()) - objects->maybeAnalyze(cx, obj->group(), /* forceAnalyze = */ true); -} - static bool GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args, MutableHandleObject res, bool* skipAttach) @@ -5528,10 +5157,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& // With this and other array templates, analyze the group so that // we don't end up with a template whose structure might change later. res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, count, TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + return !!res; } } @@ -5557,10 +5183,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& } res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0, TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + return !!res; } } } @@ -5577,10 +5200,7 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& } res.set(NewFullyAllocatedArrayForCallingAllocationSite(cx, 0, TenuredObject)); - if (!res) - return false; - EnsureArrayGroupAnalyzed(cx, res); - return true; + return !!res; } if (native == StringConstructor) { @@ -5793,7 +5413,7 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb if (!thisObject) return false; - if (thisObject->is() || thisObject->is()) + if (thisObject->is()) templateObject = thisObject; } @@ -5887,15 +5507,24 @@ TryAttachCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsb } static bool -CopyArray(JSContext* cx, HandleObject obj, MutableHandleValue result) +CopyArray(JSContext* cx, HandleArrayObject arr, MutableHandleValue result) { - uint32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); - JSObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, obj, length, TenuredObject); + uint32_t length = arr->length(); + ArrayObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, arr, length, TenuredObject); if (!nobj) return false; - EnsureArrayGroupAnalyzed(cx, nobj); - CopyAnyBoxedOrUnboxedDenseElements(cx, nobj, obj, 0, 0, length); + + MOZ_ASSERT(arr->isNative()); + MOZ_ASSERT(nobj->isNative()); + MOZ_ASSERT(nobj->as().getDenseInitializedLength() == 0); + MOZ_ASSERT(arr->as().getDenseInitializedLength() >= length); + MOZ_ASSERT(nobj->as().getDenseCapacity() >= length); + nobj->as().setDenseInitializedLength(length); + + const Value* vp = arr->as().getDenseElements(); + nobj->as().initDenseElements(0, vp, length); + result.setObject(*nobj); return true; } @@ -5926,26 +5555,22 @@ TryAttachStringSplit(JSContext* cx, ICCall_Fallback* stub, HandleScript script, RootedValue arr(cx); // Copy the array before storing in stub. - if (!CopyArray(cx, obj, &arr)) + if (!CopyArray(cx, obj.as(), &arr)) return false; // Atomize all elements of the array. - RootedObject arrObj(cx, &arr.toObject()); - uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(arrObj); + RootedArrayObject arrObj(cx, &arr.toObject().as()); + uint32_t initLength = arrObj->length(); for (uint32_t i = 0; i < initLength; i++) { - JSAtom* str = js::AtomizeString(cx, GetAnyBoxedOrUnboxedDenseElement(arrObj, i).toString()); + JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString()); if (!str) return false; - if (!SetAnyBoxedOrUnboxedDenseElement(cx, arrObj, i, StringValue(str))) { - // The value could not be stored to an unboxed dense element. - return true; - } + arrObj->setDenseElementWithType(cx, i, StringValue(str)); } ICCall_StringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(), - script->pcToOffset(pc), str, sep, - arr); + script->pcToOffset(pc), str, sep, arrObj); ICStub* newStub = compiler.getStub(compiler.getStubSpace(script)); if (!newStub) return false; @@ -6830,7 +6455,7 @@ ICCallScriptedCompiler::generateStubCode(MacroAssembler& masm) return true; } -typedef bool (*CopyArrayFn)(JSContext*, HandleObject, MutableHandleValue); +typedef bool (*CopyArrayFn)(JSContext*, HandleArrayObject, MutableHandleValue); static const VMFunction CopyArrayInfo = FunctionInfo(CopyArray, "CopyArray"); bool @@ -8301,19 +7926,6 @@ ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorSt return New(cx, space, other.jitCode(), firstMonitorStub, other.shape_); } -ICGetElem_UnboxedArray::ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, - ObjectGroup *group) - : ICMonitoredStub(GetElem_UnboxedArray, stubCode, firstMonitorStub), - group_(group) -{ } - -/* static */ ICGetElem_UnboxedArray* -ICGetElem_UnboxedArray::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub, - ICGetElem_UnboxedArray& other) -{ - return New(cx, space, other.jitCode(), firstMonitorStub, other.group_); -} - ICGetElem_TypedArray::ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type) : ICStub(GetElem_TypedArray, stubCode), shape_(shape) @@ -8689,8 +8301,8 @@ static bool DoRestFallback(JSContext* cx, BaselineFrame* frame, ICRest_Fallback* unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0; Value* rest = frame->argv() + numFormals; - JSObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject, - ObjectGroup::NewArrayKind::UnknownIndex); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, rest, numRest, GenericObject, + ObjectGroup::NewArrayKind::UnknownIndex); if (!obj) return false; res.setObject(*obj); diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index a57556d99..98f0e1c59 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -22,7 +22,6 @@ #include "jit/SharedICRegisters.h" #include "js/GCVector.h" #include "vm/ArrayObject.h" -#include "vm/UnboxedObject.h" namespace js { namespace jit { @@ -892,54 +891,6 @@ class ICGetElem_Dense : public ICMonitoredStub }; }; -class ICGetElem_UnboxedArray : public ICMonitoredStub -{ - friend class ICStubSpace; - - GCPtrObjectGroup group_; - - ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group); - - public: - static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space, - ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other); - - static size_t offsetOfGroup() { - return offsetof(ICGetElem_UnboxedArray, group_); - } - - GCPtrObjectGroup& group() { - return group_; - } - - class Compiler : public ICStubCompiler { - ICStub* firstMonitorStub_; - RootedObjectGroup group_; - JSValueType elementType_; - - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); - - virtual int32_t getKey() const { - return static_cast(engine_) | - (static_cast(kind) << 1) | - (static_cast(elementType_) << 17); - } - - public: - Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group) - : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray, Engine::Baseline), - firstMonitorStub_(firstMonitorStub), - group_(cx, group), - elementType_(group->unboxedLayoutDontCheckGeneration().elementType()) - {} - - ICStub* getStub(ICStubSpace* space) { - return newStub(space, getStubCode(), firstMonitorStub_, group_); - } - }; -}; - // Accesses scalar elements of a typed array or typed object. class ICGetElem_TypedArray : public ICStub { @@ -1115,9 +1066,7 @@ class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray, Engine::Baseline), shape_(cx, shape), group_(cx, group), - unboxedType_(shape - ? JSVAL_TYPE_MAGIC - : group->unboxedLayoutDontCheckGeneration().elementType()) + unboxedType_(JSVAL_TYPE_MAGIC) {} ICUpdatedStub* getStub(ICStubSpace* space) { @@ -1225,9 +1174,7 @@ class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler { : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArrayAdd, Engine::Baseline), obj_(cx, obj), protoChainDepth_(protoChainDepth), - unboxedType_(obj->is() - ? obj->as().elementType() - : JSVAL_TYPE_MAGIC) + unboxedType_(JSVAL_TYPE_MAGIC) {} template @@ -1875,8 +1822,7 @@ class ICSetProp_Native : public ICUpdatedStub virtual int32_t getKey() const { return static_cast(engine_) | (static_cast(kind) << 1) | - (static_cast(isFixedSlot_) << 17) | - (static_cast(obj_->is()) << 18); + (static_cast(isFixedSlot_) << 17); } MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); @@ -1981,7 +1927,6 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler return static_cast(engine_) | (static_cast(kind) << 1) | (static_cast(isFixedSlot_) << 17) | - (static_cast(obj_->is()) << 18) | (static_cast(protoChainDepth_) << 19); } @@ -2006,10 +1951,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler newGroup = nullptr; RootedShape newShape(cx); - if (obj_->isNative()) - newShape = obj_->as().lastProperty(); - else - newShape = obj_->as().maybeExpando()->lastProperty(); + newShape = obj_->as().lastProperty(); return newStub>( space, getStubCode(), oldGroup_, shapes, newShape, newGroup, offset_); @@ -2870,10 +2812,10 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; GCPtrString expectedStr_; GCPtrString expectedSep_; - GCPtrObject templateObject_; + GCPtrArrayObject templateObject_; ICCall_StringSplit(JitCode* stubCode, ICStub* firstMonitorStub, uint32_t pcOffset, JSString* str, - JSString* sep, JSObject* templateObject) + JSString* sep, ArrayObject* templateObject) : ICMonitoredStub(ICStub::Call_StringSplit, stubCode, firstMonitorStub), pcOffset_(pcOffset), expectedStr_(str), expectedSep_(sep), templateObject_(templateObject) @@ -2900,7 +2842,7 @@ class ICCall_StringSplit : public ICMonitoredStub return expectedSep_; } - GCPtrObject& templateObject() { + GCPtrArrayObject& templateObject() { return templateObject_; } @@ -2910,7 +2852,7 @@ class ICCall_StringSplit : public ICMonitoredStub uint32_t pcOffset_; RootedString expectedStr_; RootedString expectedSep_; - RootedObject templateObject_; + RootedArrayObject templateObject_; MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm); @@ -2921,13 +2863,13 @@ class ICCall_StringSplit : public ICMonitoredStub public: Compiler(JSContext* cx, ICStub* firstMonitorStub, uint32_t pcOffset, HandleString str, - HandleString sep, HandleValue templateObject) + HandleString sep, HandleArrayObject templateObject) : ICCallStubCompiler(cx, ICStub::Call_StringSplit), firstMonitorStub_(firstMonitorStub), pcOffset_(pcOffset), expectedStr_(cx, str), expectedSep_(cx, sep), - templateObject_(cx, &templateObject.toObject()) + templateObject_(cx, templateObject) { } ICStub* getStub(ICStubSpace* space) { diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index c9e09bed7..3b852debf 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -96,32 +96,19 @@ VectorAppendNoDuplicate(S& list, T value) static bool AddReceiver(const ReceiverGuard& receiver, - BaselineInspector::ReceiverVector& receivers, - BaselineInspector::ObjectGroupVector& convertUnboxedGroups) + BaselineInspector::ReceiverVector& receivers) { - if (receiver.group && receiver.group->maybeUnboxedLayout()) { - if (receiver.group->unboxedLayout().nativeGroup()) - return VectorAppendNoDuplicate(convertUnboxedGroups, receiver.group); - } return VectorAppendNoDuplicate(receivers, receiver); } static bool GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* receiver) { - // We match either: + // We match: // // GuardIsObject 0 // GuardShape 0 // LoadFixedSlotResult 0 or LoadDynamicSlotResult 0 - // - // or - // - // GuardIsObject 0 - // GuardGroup 0 - // 1: GuardAndLoadUnboxedExpando 0 - // GuardShape 1 - // LoadFixedSlotResult 1 or LoadDynamicSlotResult 1 *receiver = ReceiverGuard(); CacheIRReader reader(stub->stubInfo()); @@ -130,14 +117,6 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re if (!reader.matchOp(CacheOp::GuardIsObject, objId)) return false; - if (reader.matchOp(CacheOp::GuardGroup, objId)) { - receiver->group = stub->stubInfo()->getStubField(stub, reader.stubOffset()); - - if (!reader.matchOp(CacheOp::GuardAndLoadUnboxedExpando, objId)) - return false; - objId = reader.objOperandId(); - } - if (reader.matchOp(CacheOp::GuardShape, objId)) { receiver->shape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); return reader.matchOpEither(CacheOp::LoadFixedSlotResult, CacheOp::LoadDynamicSlotResult); @@ -146,40 +125,13 @@ GetCacheIRReceiverForNativeReadSlot(ICCacheIR_Monitored* stub, ReceiverGuard* re return false; } -static bool -GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* receiver) -{ - // We match: - // - // GuardIsObject 0 - // GuardGroup 0 - // LoadUnboxedPropertyResult 0 .. - - *receiver = ReceiverGuard(); - CacheIRReader reader(stub->stubInfo()); - - ObjOperandId objId = ObjOperandId(0); - if (!reader.matchOp(CacheOp::GuardIsObject, objId)) - return false; - - if (!reader.matchOp(CacheOp::GuardGroup, objId)) - return false; - receiver->group = stub->stubInfo()->getStubField(stub, reader.stubOffset()); - - return reader.matchOp(CacheOp::LoadUnboxedPropertyResult, objId); -} - bool -BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) +BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers) { // Return a list of the receivers seen by the baseline IC for the current // op. Empty lists indicate no receivers are known, or there was an - // uncacheable access. convertUnboxedGroups is used for unboxed object - // groups which have been seen, but have had instances converted to native - // objects and should be eagerly converted by Ion. + // uncacheable access. MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); if (!hasBaselineScript()) return true; @@ -191,8 +143,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv while (stub->next()) { ReceiverGuard receiver; if (stub->isCacheIR_Monitored()) { - if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver) && - !GetCacheIRReceiverForUnboxedProperty(stub->toCacheIR_Monitored(), &receiver)) + if (!GetCacheIRReceiverForNativeReadSlot(stub->toCacheIR_Monitored(), &receiver)) { receivers.clear(); return true; @@ -200,14 +151,12 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv } else if (stub->isSetProp_Native()) { receiver = ReceiverGuard(stub->toSetProp_Native()->group(), stub->toSetProp_Native()->shape()); - } else if (stub->isSetProp_Unboxed()) { - receiver = ReceiverGuard(stub->toSetProp_Unboxed()->group(), nullptr); } else { receivers.clear(); return true; } - if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + if (!AddReceiver(receiver, receivers)) return false; stub = stub->next(); @@ -589,7 +538,7 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode* pc, Native native) bool BaselineInspector::isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, JSString** sepOut, - JSObject** objOut) + ArrayObject** objOut) { if (!hasBaselineScript()) return false; @@ -700,14 +649,12 @@ bool BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) + ReceiverVector& receivers) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -719,7 +666,7 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap { ICGetPropCallGetter* nstub = static_cast(stub); bool isOwn = nstub->isOwnGetter(); - if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) + if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers)) return false; if (!*holder) { @@ -751,21 +698,19 @@ BaselineInspector::commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shap if (!*holder) return false; - MOZ_ASSERT(*isOwnProperty == (receivers.empty() && convertUnboxedGroups.empty())); + MOZ_ASSERT(*isOwnProperty == (receivers.empty())); return true; } bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups) + ReceiverVector& receivers) { if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); - MOZ_ASSERT(convertUnboxedGroups.empty()); *holder = nullptr; const ICEntry& entry = icEntryFromPC(pc); @@ -774,7 +719,7 @@ BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shap if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) { ICSetPropCallSetter* nstub = static_cast(stub); bool isOwn = nstub->isOwnSetter(); - if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers, convertUnboxedGroups)) + if (!isOwn && !AddReceiver(nstub->receiverGuard(), receivers)) return false; if (!*holder) { diff --git a/js/src/jit/BaselineInspector.h b/js/src/jit/BaselineInspector.h index 961df18aa..1ed4b5547 100644 --- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -95,8 +95,7 @@ class BaselineInspector public: typedef Vector ReceiverVector; typedef Vector ObjectGroupVector; - MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + MOZ_MUST_USE bool maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receivers); SetElemICInspector setElemICInspector(jsbytecode* pc) { return makeICInspector(pc, ICStub::SetElem_Fallback); @@ -114,7 +113,7 @@ class BaselineInspector bool hasSeenNonStringIterMore(jsbytecode* pc); MOZ_MUST_USE bool isOptimizableCallStringSplit(jsbytecode* pc, JSString** strOut, - JSString** sepOut, JSObject** objOut); + JSString** sepOut, ArrayObject** objOut); JSObject* getTemplateObject(jsbytecode* pc); JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native); JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp); @@ -131,12 +130,10 @@ class BaselineInspector MOZ_MUST_USE bool commonGetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonGetter, Shape** globalShape, - bool* isOwnProperty, ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + bool* isOwnProperty, ReceiverVector& receivers); MOZ_MUST_USE bool commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, - ReceiverVector& receivers, - ObjectGroupVector& convertUnboxedGroups); + ReceiverVector& receivers); MOZ_MUST_USE bool instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject); diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index f1061af70..d184ea40c 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -10,8 +10,7 @@ #include "jit/IonCaches.h" #include "jsobjinlines.h" - -#include "vm/UnboxedObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; using namespace js::jit; @@ -60,10 +59,6 @@ GetPropIRGenerator::tryAttachStub(Maybe& writer) return false; if (!emitted_ && !tryAttachNative(*writer, obj, objId)) return false; - if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId)) - return false; - if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId)) - return false; if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId)) return false; if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId)) @@ -163,19 +158,9 @@ GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, } static void -TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId, - Maybe* expandoId) +TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, Shape* shape, ObjOperandId objId) { - if (obj->is()) { - writer.guardGroup(objId, obj->group()); - - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { - expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId)); - writer.guardShape(expandoId->ref(), expando->lastProperty()); - } else { - writer.guardNoUnboxedExpando(objId); - } - } else if (obj->is() || obj->is()) { + if (obj->is()) { writer.guardGroup(objId, obj->group()); } else { Shape* shape = obj->maybeShape(); @@ -188,8 +173,7 @@ static void EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { - Maybe expandoId; - TestMatchingReceiver(writer, obj, shape, objId, &expandoId); + TestMatchingReceiver(writer, obj, shape, objId); ObjOperandId holderId; if (obj != holder) { @@ -212,9 +196,6 @@ EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, lastObjId = protoId; } } - } else if (obj->is()) { - holder = obj->as().maybeExpando(); - holderId = *expandoId; } else { holderId = objId; } @@ -265,51 +246,6 @@ GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, Obj return true; } -bool -GetPropIRGenerator::tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) -{ - MOZ_ASSERT(!emitted_); - - if (!obj->is()) - return true; - - const UnboxedLayout::Property* property = obj->as().layout().lookup(name_); - if (!property) - return true; - - if (!cx_->runtime()->jitSupportsFloatingPoint) - return true; - - writer.guardGroup(objId, obj->group()); - writer.loadUnboxedPropertyResult(objId, property->type, - UnboxedPlainObject::offsetOfData() + property->offset); - emitted_ = true; - preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; - return true; -} - -bool -GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) -{ - MOZ_ASSERT(!emitted_); - - if (!obj->is()) - return true; - - UnboxedExpandoObject* expando = obj->as().maybeExpando(); - if (!expando) - return true; - - Shape* shape = expando->lookup(cx_, NameToId(name_)); - if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - emitted_ = true; - - EmitReadSlotResult(writer, obj, obj, shape, objId); - return true; -} - bool GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { @@ -368,13 +304,6 @@ GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject ob return true; } - if (obj->is()) { - writer.guardClass(objId, GuardClassKind::UnboxedArray); - writer.loadUnboxedArrayLengthResult(objId); - emitted_ = true; - return true; - } - if (obj->is() && !obj->as().hasOverriddenLength()) { if (obj->is()) { writer.guardClass(objId, GuardClassKind::MappedArguments); diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 51e55f48b..ae55cfebb 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -87,16 +87,12 @@ class ObjOperandId : public OperandId _(GuardClass) \ _(GuardSpecificObject) \ _(GuardNoDetachedTypedObjects) \ - _(GuardNoUnboxedExpando) \ - _(GuardAndLoadUnboxedExpando) \ _(LoadObject) \ _(LoadProto) \ _(LoadFixedSlotResult) \ _(LoadDynamicSlotResult) \ - _(LoadUnboxedPropertyResult) \ _(LoadTypedObjectResult) \ _(LoadInt32ArrayLengthResult) \ - _(LoadUnboxedArrayLengthResult) \ _(LoadArgumentsObjectLengthResult) \ _(LoadUndefinedResult) @@ -128,7 +124,6 @@ struct StubField { enum class GuardClassKind { Array, - UnboxedArray, MappedArguments, UnmappedArguments, }; @@ -276,15 +271,6 @@ class MOZ_RAII CacheIRWriter void guardNoDetachedTypedObjects() { writeOp(CacheOp::GuardNoDetachedTypedObjects); } - void guardNoUnboxedExpando(ObjOperandId obj) { - writeOpWithOperandId(CacheOp::GuardNoUnboxedExpando, obj); - } - ObjOperandId guardAndLoadUnboxedExpando(ObjOperandId obj) { - ObjOperandId res(nextOperandId_++); - writeOpWithOperandId(CacheOp::GuardAndLoadUnboxedExpando, obj); - writeOperandId(res); - return res; - } ObjOperandId loadObject(JSObject* obj) { ObjOperandId res(nextOperandId_++); @@ -310,11 +296,6 @@ class MOZ_RAII CacheIRWriter writeOpWithOperandId(CacheOp::LoadDynamicSlotResult, obj); addStubWord(offset, StubField::GCType::NoGCThing); } - void loadUnboxedPropertyResult(ObjOperandId obj, JSValueType type, size_t offset) { - writeOpWithOperandId(CacheOp::LoadUnboxedPropertyResult, obj); - buffer_.writeByte(uint32_t(type)); - addStubWord(offset, StubField::GCType::NoGCThing); - } void loadTypedObjectResult(ObjOperandId obj, uint32_t offset, TypedThingLayout layout, uint32_t typeDescr) { MOZ_ASSERT(uint32_t(layout) <= UINT8_MAX); @@ -327,9 +308,6 @@ class MOZ_RAII CacheIRWriter void loadInt32ArrayLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadInt32ArrayLengthResult, obj); } - void loadUnboxedArrayLengthResult(ObjOperandId obj) { - writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj); - } void loadArgumentsObjectLengthResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj); } @@ -411,9 +389,6 @@ class MOZ_RAII GetPropIRGenerator PreliminaryObjectAction preliminaryObjectAction_; MOZ_MUST_USE bool tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); - MOZ_MUST_USE bool tryAttachUnboxed(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); - MOZ_MUST_USE bool tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, - ObjOperandId objId); MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId); MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 16d026092..fcb711237 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -25,6 +25,7 @@ #include "builtin/Eval.h" #include "builtin/TypedObject.h" #include "gc/Nursery.h" +#include "gc/StoreBuffer-inl.h" #include "irregexp/NativeRegExpMacroAssembler.h" #include "jit/AtomicOperations.h" #include "jit/BaselineCompiler.h" @@ -3027,19 +3028,10 @@ CodeGenerator::visitStoreSlotV(LStoreSlotV* lir) static void GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard, - Register obj, Register scratch, Label* miss, bool checkNullExpando) + Register obj, Register scratch, Label* miss) { if (guard.group) { masm.branchTestObjGroup(Assembler::NotEqual, obj, guard.group, miss); - - Address expandoAddress(obj, UnboxedPlainObject::offsetOfExpando()); - if (guard.shape) { - masm.loadPtr(expandoAddress, scratch); - masm.branchPtr(Assembler::Equal, scratch, ImmWord(0), miss); - masm.branchTestObjShape(Assembler::NotEqual, scratch, guard.shape, miss); - } else if (checkNullExpando) { - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), miss); - } } else { masm.branchTestObjShape(Assembler::NotEqual, obj, guard.shape, miss); } @@ -3058,13 +3050,11 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis Label next; masm.comment("GuardReceiver"); - GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); + GuardReceiver(masm, receiver, obj, scratch, &next); if (receiver.shape) { masm.comment("loadTypedOrValue"); - // If this is an unboxed expando access, GuardReceiver loaded the - // expando object into scratch. - Register target = receiver.group ? scratch : obj; + Register target = obj; Shape* shape = mir->shape(i); if (shape->slot() < shape->numFixedSlots()) { @@ -3077,13 +3067,6 @@ CodeGenerator::emitGetPropertyPolymorphic(LInstruction* ins, Register obj, Regis masm.loadPtr(Address(target, NativeObject::offsetOfSlots()), scratch); masm.loadTypedOrValue(Address(scratch, offset), output); } - } else { - masm.comment("loadUnboxedProperty"); - const UnboxedLayout::Property* property = - receiver.group->unboxedLayout().lookup(mir->name()); - Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - - masm.loadUnboxedProperty(propertyAddr, property->type, output); } if (i == mir->numReceivers() - 1) { @@ -3124,8 +3107,6 @@ EmitUnboxedPreBarrier(MacroAssembler &masm, T address, JSValueType type) masm.patchableCallPreBarrier(address, MIRType::Object); else if (type == JSVAL_TYPE_STRING) masm.patchableCallPreBarrier(address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(type)); } void @@ -3139,12 +3120,10 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis ReceiverGuard receiver = mir->receiver(i); Label next; - GuardReceiver(masm, receiver, obj, scratch, &next, /* checkNullExpando = */ false); + GuardReceiver(masm, receiver, obj, scratch, &next); if (receiver.shape) { - // If this is an unboxed expando access, GuardReceiver loaded the - // expando object into scratch. - Register target = receiver.group ? scratch : obj; + Register target = obj; Shape* shape = mir->shape(i); if (shape->slot() < shape->numFixedSlots()) { @@ -3161,13 +3140,6 @@ CodeGenerator::emitSetPropertyPolymorphic(LInstruction* ins, Register obj, Regis emitPreBarrier(addr); masm.storeConstantOrRegister(value, addr); } - } else { - const UnboxedLayout::Property* property = - receiver.group->unboxedLayout().lookup(mir->name()); - Address propertyAddr(obj, UnboxedPlainObject::offsetOfData() + property->offset); - - EmitUnboxedPreBarrier(masm, propertyAddr, property->type); - masm.storeUnboxedProperty(propertyAddr, property->type, value, nullptr); } if (i == mir->numReceivers() - 1) { @@ -3208,9 +3180,7 @@ CodeGenerator::visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins) void CodeGenerator::visitElements(LElements* lir) { - Address elements(ToRegister(lir->object()), - lir->mir()->unboxed() ? UnboxedArrayObject::offsetOfElements() - : NativeObject::offsetOfElements()); + Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); masm.loadPtr(elements, ToRegister(lir->output())); } @@ -3319,7 +3289,7 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) const ReceiverGuard& receiver = mir->receiver(i); Label next; - GuardReceiver(masm, receiver, obj, temp, &next, /* checkNullExpando = */ true); + GuardReceiver(masm, receiver, obj, temp, &next); if (i == mir->numReceivers() - 1) { bailoutFrom(&next, lir->snapshot()); @@ -3332,27 +3302,6 @@ CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir) masm.bind(&done); } -void -CodeGenerator::visitGuardUnboxedExpando(LGuardUnboxedExpando* lir) -{ - Label miss; - - Register obj = ToRegister(lir->object()); - masm.branchPtr(lir->mir()->requireExpando() ? Assembler::Equal : Assembler::NotEqual, - Address(obj, UnboxedPlainObject::offsetOfExpando()), ImmWord(0), &miss); - - bailoutFrom(&miss, lir->snapshot()); -} - -void -CodeGenerator::visitLoadUnboxedExpando(LLoadUnboxedExpando* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->getDef(0)); - - masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), result); -} - void CodeGenerator::visitTypeBarrierV(LTypeBarrierV* lir) { @@ -5217,11 +5166,11 @@ static JSObject* NewArrayWithGroup(JSContext* cx, uint32_t length, HandleObjectGroup group, bool convertDoubleElements) { - JSObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length); + ArrayObject* res = NewFullyAllocatedArrayTryUseGroup(cx, group, length); if (!res) return nullptr; if (convertDoubleElements) - res->as().setShouldConvertDoubleElements(); + res->setShouldConvertDoubleElements(); return res; } @@ -5367,7 +5316,7 @@ CodeGenerator::visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir) masm.bind(ool->rejoin()); } -typedef JSObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); +typedef ArrayObject* (*ArrayConstructorOneArgFn)(JSContext*, HandleObjectGroup, int32_t length); static const VMFunction ArrayConstructorOneArgInfo = FunctionInfo(ArrayConstructorOneArg, "ArrayConstructorOneArg"); @@ -5387,21 +5336,11 @@ CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) bool canInline = true; size_t inlineLength = 0; - if (templateObject->is()) { - if (templateObject->as().hasFixedElements()) { - size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); - inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; - } else { - canInline = false; - } + if (templateObject->as().hasFixedElements()) { + size_t numSlots = gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); + inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; } else { - if (templateObject->as().hasInlineElements()) { - size_t nbytes = - templateObject->tenuredSizeOfThis() - UnboxedArrayObject::offsetOfInlineElements(); - inlineLength = nbytes / templateObject->as().elementSize(); - } else { - canInline = false; - } + canInline = false; } if (canInline) { @@ -7823,7 +7762,7 @@ CodeGenerator::visitSinCos(LSinCos *lir) masm.freeStack(sizeof(double) * 2); } -typedef JSObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t); +typedef ArrayObject* (*StringSplitFn)(JSContext*, HandleObjectGroup, HandleString, HandleString, uint32_t); static const VMFunction StringSplitInfo = FunctionInfo(js::str_split_string, "str_split_string"); @@ -7857,49 +7796,6 @@ CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) masm.dec32(&index); } -void -CodeGenerator::visitUnboxedArrayLength(LUnboxedArrayLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), result); -} - -void -CodeGenerator::visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - Register result = ToRegister(lir->output()); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), result); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), result); -} - -void -CodeGenerator::visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); -} - -void -CodeGenerator::visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir) -{ - Register obj = ToRegister(lir->object()); - RegisterOrInt32Constant key = ToRegisterOrInt32Constant(lir->length()); - Register temp = ToRegister(lir->temp()); - - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLengthAddr, temp); - masm.and32(Imm32(UnboxedArrayObject::CapacityMask), temp); - - if (key.isRegister()) - masm.or32(key.reg(), temp); - else - masm.or32(Imm32(key.constant()), temp); - - masm.store32(temp, initLengthAddr); -} - void CodeGenerator::visitNotO(LNotO* lir) { @@ -8196,46 +8092,19 @@ CodeGenerator::emitStoreElementHoleT(T* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); - Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index, 0); - masm.bind(ool->rejoinStore()); - emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), - elements, index, 0); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry()); - - ConstantOrRegister v = ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); - - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, v, nullptr); - } - } + masm.bind(ool->rejoinStore()); + emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), lir->mir()->elementType(), + elements, index, 0); masm.bind(ool->rejoin()); } @@ -8255,47 +8124,22 @@ CodeGenerator::emitStoreElementHoleV(T* lir) OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir); addOutOfLineCode(ool, lir->mir()); - Register obj = ToRegister(lir->object()); Register elements = ToRegister(lir->elements()); const LAllocation* index = lir->index(); const ValueOperand value = ToValue(lir, T::Value); RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); - JSValueType unboxedType = lir->mir()->unboxedType(); - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::BelowOrEqual, initLength, key, ool->entry()); - if (lir->mir()->needsBarrier()) - emitPreBarrier(elements, index, 0); + if (lir->mir()->needsBarrier()) + emitPreBarrier(elements, index, 0); - masm.bind(ool->rejoinStore()); - if (index->isConstant()) - masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); - else - masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); - } else { - Register temp = ToRegister(lir->getTemp(0)); - Address initLength(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, temp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp); - masm.branch32(Assembler::BelowOrEqual, temp, key, ool->entry()); - - if (index->isConstant()) { - Address address(elements, ToInt32(index) * UnboxedTypeSize(unboxedType)); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } else { - BaseIndex address(elements, ToRegister(index), - ScaleFromElemWidth(UnboxedTypeSize(unboxedType))); - EmitUnboxedPreBarrier(masm, address, unboxedType); - - masm.bind(ool->rejoinStore()); - masm.storeUnboxedProperty(address, unboxedType, ConstantOrRegister(value), nullptr); - } - } + masm.bind(ool->rejoinStore()); + if (index->isConstant()) + masm.storeValue(value, Address(elements, ToInt32(index) * sizeof(js::Value))); + else + masm.storeValue(value, BaseIndex(elements, ToRegister(index), TimesEight)); masm.bind(ool->rejoin()); } @@ -8366,11 +8210,10 @@ CodeGenerator::visitFallibleStoreElementV(LFallibleStoreElementV* lir) masm.bind(&isFrozen); } -typedef bool (*SetDenseOrUnboxedArrayElementFn)(JSContext*, HandleObject, int32_t, - HandleValue, bool strict); -static const VMFunction SetDenseOrUnboxedArrayElementInfo = - FunctionInfo(SetDenseOrUnboxedArrayElement, - "SetDenseOrUnboxedArrayElement"); +typedef bool (*SetDenseElementFn)(JSContext*, HandleNativeObject, int32_t, HandleValue, + bool strict); +static const VMFunction SetDenseElementInfo = + FunctionInfo(jit::SetDenseElement, "SetDenseElement"); void CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) @@ -8380,8 +8223,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) const LAllocation* index; MIRType valueType; ConstantOrRegister value; - JSValueType unboxedType; - LDefinition *temp = nullptr; if (ins->isStoreElementHoleV()) { LStoreElementHoleV* store = ins->toStoreElementHoleV(); @@ -8390,8 +8231,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LStoreElementHoleV::Value)); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else if (ins->isFallibleStoreElementV()) { LFallibleStoreElementV* store = ins->toFallibleStoreElementV(); object = ToRegister(store->object()); @@ -8399,8 +8238,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) index = store->index(); valueType = store->mir()->value()->type(); value = TypedOrValueRegister(ToValue(store, LFallibleStoreElementV::Value)); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else if (ins->isStoreElementHoleT()) { LStoreElementHoleT* store = ins->toStoreElementHoleT(); object = ToRegister(store->object()); @@ -8411,8 +8248,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } else { // ins->isFallibleStoreElementT() LFallibleStoreElementT* store = ins->toFallibleStoreElementT(); object = ToRegister(store->object()); @@ -8423,8 +8258,6 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) value = ConstantOrRegister(store->value()->toConstant()->toJSValue()); else value = TypedOrValueRegister(valueType, ToAnyRegister(store->value())); - unboxedType = store->mir()->unboxedType(); - temp = store->getTemp(0); } RegisterOrInt32Constant key = ToRegisterOrInt32Constant(index); @@ -8435,54 +8268,32 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) Label callStub; #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) // Had to reimplement for MIPS because there are no flags. - if (unboxedType == JSVAL_TYPE_MAGIC) { - Address initLength(elements, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, &callStub); - } else { - Address initLength(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - masm.load32(initLength, ToRegister(temp)); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), ToRegister(temp)); - masm.branch32(Assembler::NotEqual, ToRegister(temp), key, &callStub); - } + Address initLength(elements, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, &callStub); #else masm.j(Assembler::NotEqual, &callStub); #endif - if (unboxedType == JSVAL_TYPE_MAGIC) { - // Check array capacity. - masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), - key, &callStub); + // Check array capacity. + masm.branch32(Assembler::BelowOrEqual, Address(elements, ObjectElements::offsetOfCapacity()), + key, &callStub); - // Update initialized length. The capacity guard above ensures this won't overflow, - // due to MAX_DENSE_ELEMENTS_COUNT. - masm.inc32(&key); - masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength())); + // Update initialized length. The capacity guard above ensures this won't overflow, + // due to MAX_DENSE_ELEMENTS_COUNT. + masm.inc32(&key); + masm.store32(key, Address(elements, ObjectElements::offsetOfInitializedLength())); - // Update length if length < initializedLength. - Label dontUpdate; - masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), - key, &dontUpdate); - masm.store32(key, Address(elements, ObjectElements::offsetOfLength())); - masm.bind(&dontUpdate); + // Update length if length < initializedLength. + Label dontUpdate; + masm.branch32(Assembler::AboveOrEqual, Address(elements, ObjectElements::offsetOfLength()), + key, &dontUpdate); + masm.store32(key, Address(elements, ObjectElements::offsetOfLength())); + masm.bind(&dontUpdate); - masm.dec32(&key); - } else { - // Check array capacity. - masm.checkUnboxedArrayCapacity(object, key, ToRegister(temp), &callStub); - - // Update initialized length. - masm.add32(Imm32(1), Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - - // Update length if length < initializedLength. - Address lengthAddr(object, UnboxedArrayObject::offsetOfLength()); - Label dontUpdate; - masm.branch32(Assembler::Above, lengthAddr, key, &dontUpdate); - masm.add32(Imm32(1), lengthAddr); - masm.bind(&dontUpdate); - } + masm.dec32(&key); if ((ins->isStoreElementHoleT() || ins->isFallibleStoreElementT()) && - unboxedType == JSVAL_TYPE_MAGIC && valueType != MIRType::Double) + valueType != MIRType::Double) { // The inline path for StoreElementHoleT and FallibleStoreElementT does not always store // the type tag, so we do the store on the OOL path. We use MIRType::None for the element @@ -8511,7 +8322,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool) else pushArg(ToRegister(index)); pushArg(object); - callVM(SetDenseOrUnboxedArrayElementInfo, ins); + callVM(SetDenseElementInfo, ins); restoreLive(ins); masm.jump(ool->rejoin()); @@ -8568,29 +8379,6 @@ CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir) } } -typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*); -static const VMFunction ConvertUnboxedPlainObjectToNativeInfo = - FunctionInfo(UnboxedPlainObject::convertToNative, - "UnboxedPlainObject::convertToNative"); -static const VMFunction ConvertUnboxedArrayObjectToNativeInfo = - FunctionInfo(UnboxedArrayObject::convertToNative, - "UnboxedArrayObject::convertToNative"); - -void -CodeGenerator::visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir) -{ - Register object = ToRegister(lir->getOperand(0)); - - OutOfLineCode* ool = oolCallVM(lir->mir()->group()->unboxedLayoutDontCheckGeneration().isArray() - ? ConvertUnboxedArrayObjectToNativeInfo - : ConvertUnboxedPlainObjectToNativeInfo, - lir, ArgList(object), StoreNothing()); - - masm.branchPtr(Assembler::Equal, Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(lir->mir()->group()), ool->entry()); - masm.bind(ool->rejoin()); -} - typedef bool (*ArrayPopShiftFn)(JSContext*, HandleObject, MutableHandleValue); static const VMFunction ArrayPopDenseInfo = FunctionInfo(jit::ArrayPopDense, "ArrayPopDense"); @@ -8615,20 +8403,11 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R // Load elements and length, and VM call if length != initializedLength. RegisterOrInt32Constant key = RegisterOrInt32Constant(lengthTemp); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); - masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp); - Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); - } else { - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp); - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp); - - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry()); - } + Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); // Test for length != 0. On zero length either take a VM call or generate // an undefined value, depending on whether the call is known to produce @@ -8640,13 +8419,10 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R // According to the spec we need to set the length 0 (which is already 0). // This is observable when the array length is made non-writable. - // Handle this case in the OOL. When freezing an unboxed array it is converted - // to an normal array. - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); - Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); - masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); - } + // Handle this case in the OOL. + Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); + Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); + masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); masm.moveValue(UndefinedValue(), out.valueReg()); masm.jump(&done); @@ -8658,41 +8434,25 @@ CodeGenerator::emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, R masm.dec32(&key); if (mir->mode() == MArrayPopShift::Pop) { - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - BaseIndex addr(elementsTemp, lengthTemp, TimesEight); - masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); - } else { - size_t elemSize = UnboxedTypeSize(mir->unboxedType()); - BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize)); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } + BaseIndex addr(elementsTemp, lengthTemp, TimesEight); + masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } else { MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift); Address addr(elementsTemp, 0); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) - masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); - else - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); + masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry()); } - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - // Handle the failure case when the array length is non-writable in the - // OOL path. (Unlike in the adding-an-element cases, we can't rely on the - // capacity <= length invariant for such arrays to avoid an explicit - // check.) - Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); - Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); - masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); + // Handle the failure case when the array length is non-writable in the + // OOL path. (Unlike in the adding-an-element cases, we can't rely on the + // capacity <= length invariant for such arrays to avoid an explicit + // check.) + Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags()); + Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH); + masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry()); - // Now adjust length and initializedLength. - masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength())); - masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); - } else { - // Unboxed arrays always have writable lengths. Adjust length and - // initializedLength. - masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength())); - masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - } + // Now adjust length and initializedLength. + masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength())); + masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); if (mir->mode() == MArrayPopShift::Shift) { // Don't save the temp registers. @@ -8731,7 +8491,7 @@ CodeGenerator::visitArrayPopShiftT(LArrayPopShiftT* lir) emitArrayPopShift(lir, lir->mir(), obj, elements, length, out); } -typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*); +typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*); static const VMFunction ArrayPushDenseInfo = FunctionInfo(jit::ArrayPushDense, "ArrayPushDense"); @@ -8742,50 +8502,27 @@ CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, ArgList(obj, value), StoreRegisterTo(length)); RegisterOrInt32Constant key = RegisterOrInt32Constant(length); - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - // Load elements and length. - masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); - masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); - // Guard length == initializedLength. - Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); - masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); + // Load elements and length. + masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); + masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length); - // Guard length < capacity. - Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); - masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry()); + // Guard length == initializedLength. + Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength()); + masm.branch32(Assembler::NotEqual, initLength, key, ool->entry()); - // Do the store. - masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); - } else { - // Load initialized length. - masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length); + // Guard length < capacity. + Address capacity(elementsTemp, ObjectElements::offsetOfCapacity()); + masm.branch32(Assembler::BelowOrEqual, capacity, key, ool->entry()); - // Guard length == initializedLength. - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - masm.branch32(Assembler::NotEqual, lengthAddr, key, ool->entry()); - - // Guard length < capacity. - masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry()); - - // Load elements and do the store. - masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp); - size_t elemSize = UnboxedTypeSize(mir->unboxedType()); - BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize)); - masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr); - } + // Do the store. + masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight)); masm.inc32(&key); // Update length and initialized length. - if (mir->unboxedType() == JSVAL_TYPE_MAGIC) { - masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); - masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); - } else { - masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength())); - masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); - } + masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); + masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength())); masm.bind(ool->rejoin()); } @@ -8934,11 +8671,11 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir) masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2); // Compare object with the first receiver guard. The last iterator can only - // match for native objects and unboxed objects. + // match for native objects. { Address groupAddr(temp2, offsetof(ReceiverGuard, group)); Address shapeAddr(temp2, offsetof(ReceiverGuard, shape)); - Label guardDone, shapeMismatch, noExpando; + Label guardDone, shapeMismatch; masm.loadObjShape(obj, temp1); masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch); @@ -8950,12 +8687,6 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir) masm.bind(&shapeMismatch); masm.loadObjGroup(obj, temp1); masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry()); - masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1); - masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando); - branchIfNotEmptyObjectElements(temp1, ool->entry()); - masm.loadObjShape(temp1, temp1); - masm.bind(&noExpando); - masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry()); masm.bind(&guardDone); } @@ -10590,22 +10321,11 @@ CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) else masm.branch32(Assembler::BelowOrEqual, initLength, ToRegister(lir->index()), &undefined); - if (mir->unboxedType() != JSVAL_TYPE_MAGIC) { - size_t width = UnboxedTypeSize(mir->unboxedType()); - if (lir->index()->isConstant()) { - Address addr(elements, ToInt32(lir->index()) * width); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } else { - BaseIndex addr(elements, ToRegister(lir->index()), ScaleFromElemWidth(width)); - masm.loadUnboxedProperty(addr, mir->unboxedType(), out); - } + if (lir->index()->isConstant()) { + NativeObject::elementsSizeMustNotOverflow(); + masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); } else { - if (lir->index()->isConstant()) { - NativeObject::elementsSizeMustNotOverflow(); - masm.loadValue(Address(elements, ToInt32(lir->index()) * sizeof(Value)), out); - } else { - masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); - } + masm.loadValue(BaseObjectElementIndex(elements, ToRegister(lir->index())), out); } // If a hole check is needed, and the value wasn't a hole, we're done. @@ -10983,7 +10703,7 @@ CodeGenerator::visitInArray(LInArray* lir) } masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), failedInitLength); - if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { + if (mir->needsHoleCheck()) { NativeObject::elementsSizeMustNotOverflow(); Address address = Address(elements, index * sizeof(Value)); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); @@ -10996,7 +10716,7 @@ CodeGenerator::visitInArray(LInArray* lir) failedInitLength = &negativeIntCheck; masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); - if (mir->needsHoleCheck() && mir->unboxedType() == JSVAL_TYPE_MAGIC) { + if (mir->needsHoleCheck()) { BaseIndex address = BaseIndex(elements, ToRegister(lir->index()), TimesEight); masm.branchTestMagic(Assembler::Equal, address, &falseBranch); } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index b226f6cc9..b5f170d84 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -148,8 +148,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir); void visitGuardObjectIdentity(LGuardObjectIdentity* guard); void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic* lir); - void visitGuardUnboxedExpando(LGuardUnboxedExpando* lir); - void visitLoadUnboxedExpando(LLoadUnboxedExpando* lir); void visitTypeBarrierV(LTypeBarrierV* lir); void visitTypeBarrierO(LTypeBarrierO* lir); void visitMonitorTypes(LMonitorTypes* lir); @@ -236,10 +234,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitSubstr(LSubstr* lir); void visitInitializedLength(LInitializedLength* lir); void visitSetInitializedLength(LSetInitializedLength* lir); - void visitUnboxedArrayLength(LUnboxedArrayLength* lir); - void visitUnboxedArrayInitializedLength(LUnboxedArrayInitializedLength* lir); - void visitIncrementUnboxedArrayInitializedLength(LIncrementUnboxedArrayInitializedLength* lir); - void visitSetUnboxedArrayInitializedLength(LSetUnboxedArrayInitializedLength* lir); void visitNotO(LNotO* ins); void visitNotV(LNotV* ins); void visitBoundsCheck(LBoundsCheck* lir); @@ -310,7 +304,6 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitFallibleStoreElementV(LFallibleStoreElementV* lir); void visitFallibleStoreElementT(LFallibleStoreElementT* lir); void visitStoreUnboxedPointer(LStoreUnboxedPointer* lir); - void visitConvertUnboxedObjectToNative(LConvertUnboxedObjectToNative* lir); void emitArrayPopShift(LInstruction* lir, const MArrayPopShift* mir, Register obj, Register elementsTemp, Register lengthTemp, TypedOrValueRegister out); void visitArrayPopShiftV(LArrayPopShiftV* lir); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index d255c32a8..41c71c9c3 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -3515,8 +3515,6 @@ PassthroughOperand(MDefinition* def) return def->toConvertElementsToDoubles()->elements(); if (def->isMaybeCopyElementsForWrite()) return def->toMaybeCopyElementsForWrite()->object(); - if (def->isConvertUnboxedObjectToNative()) - return def->toConvertUnboxedObjectToNative()->object(); return nullptr; } diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 54d05cac4..a54a58add 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -32,7 +32,6 @@ #include "vm/EnvironmentObject-inl.h" #include "vm/NativeObject-inl.h" #include "vm/ObjectGroup-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; @@ -2219,6 +2218,8 @@ IonBuilder::inspectOpcode(JSOp op) // update that stale value. #endif default: + // Any unused opcodes and JSOP_LIMIT will end up here without having + // to explicitly specify break; } @@ -6392,7 +6393,7 @@ IonBuilder::createThisScriptedSingleton(JSFunction* target, MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is() && !templateObject->is()) + if (!templateObject->is()) return nullptr; if (templateObject->staticPrototype() != proto) return nullptr; @@ -6429,7 +6430,7 @@ IonBuilder::createThisScriptedBaseline(MDefinition* callee) JSObject* templateObject = inspector->getTemplateObject(pc); if (!templateObject) return nullptr; - if (!templateObject->is() && !templateObject->is()) + if (!templateObject->is()) return nullptr; Shape* shape = target->lookupPure(compartment->runtime()->names().prototype); @@ -7337,12 +7338,6 @@ IonBuilder::newArrayTryTemplateObject(bool* emitted, JSObject* templateObject, u if (!templateObject) return true; - if (templateObject->is()) { - MOZ_ASSERT(templateObject->as().capacity() >= length); - if (!templateObject->as().hasInlineElements()) - return true; - } - MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT); size_t arraySlots = @@ -7598,7 +7593,6 @@ IonBuilder::jsop_initelem_array() // intializer, and that arrays are marked as non-packed when writing holes // to them during initialization. bool needStub = false; - JSValueType unboxedType = JSVAL_TYPE_MAGIC; if (shouldAbortOnPreliminaryGroups(obj)) { needStub = true; } else if (!obj->resultTypeSet() || @@ -7609,12 +7603,6 @@ IonBuilder::jsop_initelem_array() } else { MOZ_ASSERT(obj->resultTypeSet()->getObjectCount() == 1); TypeSet::ObjectKey* initializer = obj->resultTypeSet()->getObject(0); - if (initializer->clasp() == &UnboxedArrayObject::class_) { - if (initializer->group()->unboxedLayout().nativeGroup()) - needStub = true; - else - unboxedType = initializer->group()->unboxedLayout().elementType(); - } if (value->type() == MIRType::MagicHole) { if (!initializer->hasFlags(constraints(), OBJECT_FLAG_NON_PACKED)) needStub = true; @@ -7634,60 +7622,46 @@ IonBuilder::jsop_initelem_array() return resumeAfter(store); } - return initializeArrayElement(obj, index, value, unboxedType, /* addResumePoint = */ true); + return initializeArrayElement(obj, index, value, /* addResumePoint = */ true); } bool IonBuilder::initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, - JSValueType unboxedType, bool addResumePointAndIncrementInitializedLength) { MConstant* id = MConstant::New(alloc(), Int32Value(index)); current->add(id); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); - if (unboxedType != JSVAL_TYPE_MAGIC) { - // Note: storeUnboxedValue takes care of any post barriers on the value. - storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false); + if (NeedsPostBarrier(value)) + current->add(MPostWriteBarrier::New(alloc(), obj, value)); - if (addResumePointAndIncrementInitializedLength) { - MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj); - current->add(increment); + if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || + (obj->isNullarySharedStub() && + obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) + { + MInstruction* valueDouble = MToDouble::New(alloc(), value); + current->add(valueDouble); + value = valueDouble; + } - if (!resumeAfter(increment)) - return false; - } - } else { - if (NeedsPostBarrier(value)) - current->add(MPostWriteBarrier::New(alloc(), obj, value)); - - if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) || - (obj->isNullarySharedStub() && - obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles)) - { - MInstruction* valueDouble = MToDouble::New(alloc(), value); - current->add(valueDouble); - value = valueDouble; - } - - // Store the value. - MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, + // Store the value. + MStoreElement* store = MStoreElement::New(alloc(), elements, id, value, /* needsHoleCheck = */ false); - current->add(store); + current->add(store); - if (addResumePointAndIncrementInitializedLength) { - // Update the initialized length. (The template object for this - // array has the array's ultimate length, so the length field is - // already correct: no updating needed.) - MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); - current->add(initLength); + if (addResumePointAndIncrementInitializedLength) { + // Update the initialized length. (The template object for this + // array has the array's ultimate length, so the length field is + // already correct: no updating needed.) + MSetInitializedLength* initLength = MSetInitializedLength::New(alloc(), elements, id); + current->add(initLength); - if (!resumeAfter(initLength)) - return false; - } + if (!resumeAfter(initLength)) + return false; } return true; @@ -7718,8 +7692,6 @@ IonBuilder::jsop_initprop(PropertyName* name) if (templateObject->is()) { if (!templateObject->as().containsPure(name)) useSlowPath = true; - } else { - MOZ_ASSERT(templateObject->as().layout().lookup(name)); } } else { useSlowPath = true; @@ -8178,9 +8150,7 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins) static bool ClassHasEffectlessLookup(const Class* clasp) { - return (clasp == &UnboxedPlainObject::class_) || - (clasp == &UnboxedArrayObject::class_) || - IsTypedObjectClass(clasp) || + return IsTypedObjectClass(clasp) || (clasp->isNative() && !clasp->getOpsLookupProperty()); } @@ -9012,8 +8982,6 @@ IonBuilder::jsop_getelem() } obj = maybeUnboxForPropertyAccess(obj); - if (obj->type() == MIRType::Object) - obj = convertUnboxedObjects(obj); bool emitted = false; @@ -9458,12 +9426,9 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), obj, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), obj, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } // Don't generate a fast path if there have been bounds check failures @@ -9480,7 +9445,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index) return true; } - if (!jsop_getelem_dense(obj, index, unboxedType)) + if (!jsop_getelem_dense(obj, index)) return false; trackOptimizationSuccess(); @@ -9832,7 +9797,7 @@ IonBuilder::computeHeapType(const TemporaryTypeSet* objTypes, const jsid id) } bool -IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType) +IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index) { TemporaryTypeSet* types = bytecodeTypes(pc); @@ -9856,7 +9821,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType !ElementAccessHasExtraIndexedProperty(this, obj); MIRType knownType = MIRType::Value; - if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier) + if (barrier == BarrierKind::NoBarrier) knownType = GetElemKnownType(needsHoleCheck, types); // Ensure index is an integer. @@ -9865,13 +9830,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType index = idInt32; // Get the elements vector. - MInstruction* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MInstruction* elements = MElements::New(alloc(), obj); current->add(elements); // Note: to help GVN, use the original MElements instruction and not // MConvertElementsToDoubles as operand. This is fine because converting // elements to double does not change the initialized length. - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); // If we can load the element as a definite double, make sure to check that // the array has been converted to homogenous doubles first. @@ -9887,7 +9852,6 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType } bool loadDouble = - unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier && loopDepth_ && inBounds && @@ -9906,18 +9870,13 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType // hoisting. index = addBoundsCheck(index, initLength); - if (unboxedType != JSVAL_TYPE_MAGIC) { - load = loadUnboxedValue(elements, 0, index, unboxedType, barrier, types); - } else { - load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); - current->add(load); - } + load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble); + current->add(load); } else { // This load may return undefined, so assume that we *can* read holes, // or that we can read out-of-bounds accesses. In this case, the bounds // check is part of the opcode. - load = MLoadElementHole::New(alloc(), elements, index, initLength, - unboxedType, needsHoleCheck); + load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck); current->add(load); // If maybeUndefined was true, the typeset must have undefined, and @@ -9927,8 +9886,7 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType } if (knownType != MIRType::Value) { - if (unboxedType == JSVAL_TYPE_MAGIC) - load->setResultType(knownType); + load->setResultType(knownType); load->setResultTypeSet(types); } @@ -10137,7 +10095,7 @@ IonBuilder::jsop_setelem() MDefinition* value = current->pop(); MDefinition* index = current->pop(); - MDefinition* object = convertUnboxedObjects(current->pop()); + MDefinition* object = current->pop(); trackTypeInfo(TrackedTypeSite::Receiver, object->type(), object->resultTypeSet()); trackTypeInfo(TrackedTypeSite::Index, index->type(), index->resultTypeSet()); @@ -10375,12 +10333,9 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, { MOZ_ASSERT(*emitted == false); - JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), object, index)) { - trackOptimizationOutcome(TrackedOutcome::AccessNotDense); - return true; - } + if (!ElementAccessIsDenseNative(constraints(), object, index)) { + trackOptimizationOutcome(TrackedOutcome::AccessNotDense); + return true; } if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, @@ -10414,7 +10369,7 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object, } // Emit dense setelem variant. - if (!jsop_setelem_dense(conversion, object, index, value, unboxedType, writeHole, emitted)) + if (!jsop_setelem_dense(conversion, object, index, value, writeHole, emitted)) return false; if (!*emitted) { @@ -10504,13 +10459,11 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object, bool IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* obj, MDefinition* id, MDefinition* value, - JSValueType unboxedType, bool writeHole, bool* emitted) + bool writeHole, bool* emitted) { MOZ_ASSERT(*emitted == false); - MIRType elementType = MIRType::None; - if (unboxedType == JSVAL_TYPE_MAGIC) - elementType = DenseNativeElementType(constraints(), obj); + MIRType elementType = DenseNativeElementType(constraints(), obj); bool packed = ElementAccessIsPacked(constraints(), obj); // Writes which are on holes in the object do not have to bail out if they @@ -10540,7 +10493,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); // Ensure the value is a double, if double conversion might be needed. @@ -10577,7 +10530,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MInstruction* store; MStoreElementCommon* common = nullptr; if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) { - MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType); + MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue); store = ins; common = ins; @@ -10589,27 +10542,23 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, bool strict = IsStrictSetPC(pc); MFallibleStoreElement* ins = MFallibleStoreElement::New(alloc(), obj, elements, id, - newValue, unboxedType, strict); + newValue, strict); store = ins; common = ins; current->add(ins); current->push(value); } else { - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); id = addBoundsCheck(id, initLength); bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty; - if (unboxedType != JSVAL_TYPE_MAGIC) { - store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue); - } else { - MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); - store = ins; - common = ins; + MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck); + store = ins; + common = ins; - current->add(store); - } + current->add(store); current->push(value); } @@ -10727,18 +10676,6 @@ IonBuilder::jsop_length_fastPath() return true; } - // Compute the length for unboxed array objects. - if (UnboxedArrayElementType(constraints(), obj, nullptr) != JSVAL_TYPE_MAGIC && - !objTypes->hasObjectFlags(constraints(), OBJECT_FLAG_LENGTH_OVERFLOW)) - { - current->pop(); - - MUnboxedArrayLength* length = MUnboxedArrayLength::New(alloc(), obj); - current->add(length); - current->push(length); - return true; - } - // Compute the length for array typed objects. TypedObjectPrediction prediction = typedObjectPrediction(obj); if (!prediction.isUseless()) { @@ -10972,11 +10909,8 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ } // Definite slots will always be fixed slots when they are in the - // allowable range for fixed slots, except for objects which were - // converted from unboxed objects and have a smaller allocation size. + // allowable range for fixed slots. size_t nfixed = NativeObject::MAX_FIXED_SLOTS; - if (ObjectGroup* group = key->group()->maybeOriginalUnboxedGroup()) - nfixed = gc::GetGCKindSlots(group->unboxedLayout().getAllocKind()); uint32_t propertySlot = property.maybeTypes()->definiteSlot(); if (slot == UINT32_MAX) { @@ -10991,65 +10925,6 @@ IonBuilder::getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_ return slot; } -uint32_t -IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValueType* punboxedType) -{ - if (!types || types->unknownObject() || !types->objectOrSentinel()) { - trackOptimizationOutcome(TrackedOutcome::NoTypeInfo); - return UINT32_MAX; - } - - uint32_t offset = UINT32_MAX; - - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (key->unknownProperties()) { - trackOptimizationOutcome(TrackedOutcome::UnknownProperties); - return UINT32_MAX; - } - - if (key->isSingleton()) { - trackOptimizationOutcome(TrackedOutcome::Singleton); - return UINT32_MAX; - } - - UnboxedLayout* layout = key->group()->maybeUnboxedLayout(); - if (!layout) { - trackOptimizationOutcome(TrackedOutcome::NotUnboxed); - return UINT32_MAX; - } - - const UnboxedLayout::Property* property = layout->lookup(name); - if (!property) { - trackOptimizationOutcome(TrackedOutcome::StructNoField); - return UINT32_MAX; - } - - if (layout->nativeGroup()) { - trackOptimizationOutcome(TrackedOutcome::UnboxedConvertedToNative); - return UINT32_MAX; - } - - key->watchStateChangeForUnboxedConvertedToNative(constraints()); - - if (offset == UINT32_MAX) { - offset = property->offset; - *punboxedType = property->type; - } else if (offset != property->offset) { - trackOptimizationOutcome(TrackedOutcome::InconsistentFieldOffset); - return UINT32_MAX; - } else if (*punboxedType != property->type) { - trackOptimizationOutcome(TrackedOutcome::InconsistentFieldType); - return UINT32_MAX; - } - } - - return offset; -} - bool IonBuilder::jsop_runonce() { @@ -11496,8 +11371,6 @@ IonBuilder::jsop_getprop(PropertyName* name) } obj = maybeUnboxForPropertyAccess(obj); - if (obj->type() == MIRType::Object) - obj = convertUnboxedObjects(obj); BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj, name, types); @@ -11569,11 +11442,6 @@ IonBuilder::jsop_getprop(PropertyName* name) if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types) || emitted) return emitted; - // Try to emit loads from unboxed objects. - trackOptimizationAttempt(TrackedStrategy::GetProp_Unboxed); - if (!getPropTryUnboxed(&emitted, obj, name, barrier, types) || emitted) - return emitted; - // Try to inline a common property getter, or make a call. trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter); if (!getPropTryCommonGetter(&emitted, obj, name, types) || emitted) @@ -11939,49 +11807,6 @@ IonBuilder::getPropTryComplexPropOfTypedObject(bool* emitted, fieldPrediction, fieldTypeObj); } -MDefinition* -IonBuilder::convertUnboxedObjects(MDefinition* obj) -{ - // If obj might be in any particular unboxed group which should be - // converted to a native representation, perform that conversion. This does - // not guarantee the object will not have such a group afterwards, if the - // object's possible groups are not precisely known. - TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->unknownObject() || !types->objectOrSentinel()) - return obj; - - BaselineInspector::ObjectGroupVector list(alloc()); - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = obj->resultTypeSet()->getObject(i); - if (!key || !key->isGroup()) - continue; - - if (UnboxedLayout* layout = key->group()->maybeUnboxedLayout()) { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (layout->nativeGroup() && !list.append(key->group())) - oomUnsafe.crash("IonBuilder::convertUnboxedObjects"); - } - } - - return convertUnboxedObjects(obj, list); -} - -MDefinition* -IonBuilder::convertUnboxedObjects(MDefinition* obj, - const BaselineInspector::ObjectGroupVector& list) -{ - for (size_t i = 0; i < list.length(); i++) { - ObjectGroup* group = list[i]; - if (TemporaryTypeSet* types = obj->resultTypeSet()) { - if (!types->hasType(TypeSet::ObjectType(group))) - continue; - } - obj = MConvertUnboxedObjectToNative::New(alloc(), obj, group); - current->add(obj->toInstruction()); - } - return obj; -} - bool IonBuilder::getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types) @@ -12066,111 +11891,14 @@ IonBuilder::getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyN return true; } -MInstruction* -IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types) -{ - // loadUnboxedValue is designed to load any value as if it were contained in - // an array. Thus a property offset is converted to an index, when the - // object is reinterpreted as an array of properties of the same size. - size_t index = offset / UnboxedTypeSize(unboxedType); - MInstruction* indexConstant = MConstant::New(alloc(), Int32Value(index)); - current->add(indexConstant); - - return loadUnboxedValue(obj, UnboxedPlainObject::offsetOfData(), - indexConstant, unboxedType, barrier, types); -} - -MInstruction* -IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* index, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types) -{ - MInstruction* load; - switch (unboxedType) { - case JSVAL_TYPE_BOOLEAN: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Uint8, - DoesNotRequireMemoryBarrier, elementsOffset); - load->setResultType(MIRType::Boolean); - break; - - case JSVAL_TYPE_INT32: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Int32, - DoesNotRequireMemoryBarrier, elementsOffset); - load->setResultType(MIRType::Int32); - break; - - case JSVAL_TYPE_DOUBLE: - load = MLoadUnboxedScalar::New(alloc(), elements, index, Scalar::Float64, - DoesNotRequireMemoryBarrier, elementsOffset, - /* canonicalizeDoubles = */ false); - load->setResultType(MIRType::Double); - break; - - case JSVAL_TYPE_STRING: - load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset); - break; - - case JSVAL_TYPE_OBJECT: { - MLoadUnboxedObjectOrNull::NullBehavior nullBehavior; - if (types->hasType(TypeSet::NullType())) - nullBehavior = MLoadUnboxedObjectOrNull::HandleNull; - else if (barrier != BarrierKind::NoBarrier) - nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull; - else - nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible; - load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior, - elementsOffset); - break; - } - - default: - MOZ_CRASH(); - } - - current->add(load); - return load; -} - -bool -IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types) -{ - MOZ_ASSERT(*emitted == false); - - JSValueType unboxedType; - uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); - if (offset == UINT32_MAX) - return true; - - if (obj->type() != MIRType::Object) { - MGuardObject* guard = MGuardObject::New(alloc(), obj); - current->add(guard); - obj = guard; - } - - MInstruction* load = loadUnboxedProperty(obj, offset, unboxedType, barrier, types); - current->push(load); - - if (!pushTypeBarrier(load, types, barrier)) - return false; - - trackOptimizationSuccess(); - *emitted = true; - return true; -} - MDefinition* IonBuilder::addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, const BaselineInspector::ReceiverVector& receivers, - const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty) { MOZ_ASSERT(holder); MOZ_ASSERT(holderShape); - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - if (isOwnProperty) { MOZ_ASSERT(receivers.empty()); return addShapeGuard(obj, holderShape, Bailout_ShapeGuard); @@ -12194,10 +11922,8 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName JSObject* foundProto = nullptr; bool isOwnProperty = false; BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter, - &globalShape, &isOwnProperty, - receivers, convertUnboxedGroups)) + &globalShape, &isOwnProperty, receivers)) { return true; } @@ -12213,8 +11939,7 @@ IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName // If type information is bad, we can still optimize the getter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receivers, convertUnboxedGroups, - isOwnProperty); + receivers, isOwnProperty); if (!obj) return false; } @@ -12381,15 +12106,12 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName MOZ_ASSERT(*emitted == false); BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) + if (!inspector->maybeInfoForPropertyOp(pc, receivers)) return false; if (!canInlinePropertyOpShapes(receivers)) return true; - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - MIRType rvalType = types->getKnownMIRType(); if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType)) rvalType = MIRType::Value; @@ -12412,45 +12134,6 @@ IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName return true; } - if (receivers[0].shape) { - // Monomorphic load from an unboxed object expando. - spew("Inlining monomorphic unboxed expando GETPROP"); - - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); - - MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); - current->add(expando); - - expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); - - Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); - - if (!loadSlot(expando, shape, rvalType, barrier, types)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; - return true; - } - - // Monomorphic load from an unboxed object. - ObjectGroup* group = receivers[0].group; - if (obj->resultTypeSet() && !obj->resultTypeSet()->hasType(TypeSet::ObjectType(group))) - return true; - - obj = addGroupGuard(obj, group, Bailout_ShapeGuard); - - const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); - MInstruction* load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types); - current->push(load); - - if (!pushTypeBarrier(load, types, barrier)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; return true; } @@ -12692,7 +12375,7 @@ bool IonBuilder::jsop_setprop(PropertyName* name) { MDefinition* value = current->pop(); - MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* obj = current->pop(); bool emitted = false; startTrackingOptimizations(); @@ -12725,13 +12408,6 @@ IonBuilder::jsop_setprop(PropertyName* name) bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value, /* canModify = */ true); - if (!forceInlineCaches()) { - // Try to emit stores to unboxed objects. - trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed); - if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted) - return emitted; - } - // Add post barrier if needed. The instructions above manage any post // barriers they need directly. if (NeedsPostBarrier(value)) @@ -12765,10 +12441,8 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, JSObject* foundProto = nullptr; bool isOwnProperty; BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter, - &isOwnProperty, - receivers, convertUnboxedGroups)) + &isOwnProperty, receivers)) { trackOptimizationOutcome(TrackedOutcome::NoProtoFound); return true; @@ -12783,8 +12457,7 @@ IonBuilder::setPropTryCommonSetter(bool* emitted, MDefinition* obj, // If type information is bad, we can still optimize the setter if we // shape guard. obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, - receivers, convertUnboxedGroups, - isOwnProperty); + receivers, isOwnProperty); if (!obj) return false; } @@ -13039,100 +12712,6 @@ IonBuilder::setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, return true; } -MInstruction* -IonBuilder::storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - MDefinition* value) -{ - size_t scaledOffsetConstant = offset / UnboxedTypeSize(unboxedType); - MInstruction* scaledOffset = MConstant::New(alloc(), Int32Value(scaledOffsetConstant)); - current->add(scaledOffset); - - return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(), - scaledOffset, unboxedType, value); -} - -MInstruction* -IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value, bool preBarrier /* = true */) -{ - MInstruction* store; - switch (unboxedType) { - case JSVAL_TYPE_BOOLEAN: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_INT32: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Int32, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_DOUBLE: - store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64, - MStoreUnboxedScalar::DontTruncateInput, - DoesNotRequireMemoryBarrier, elementsOffset); - break; - - case JSVAL_TYPE_STRING: - store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, - elementsOffset, preBarrier); - break; - - case JSVAL_TYPE_OBJECT: - MOZ_ASSERT(value->type() == MIRType::Object || - value->type() == MIRType::Null || - value->type() == MIRType::Value); - MOZ_ASSERT(!value->mightBeType(MIRType::Undefined), - "MToObjectOrNull slow path is invalid for unboxed objects"); - store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj, - elementsOffset, preBarrier); - break; - - default: - MOZ_CRASH(); - } - - current->add(store); - return store; -} - -bool -IonBuilder::setPropTryUnboxed(bool* emitted, MDefinition* obj, - PropertyName* name, MDefinition* value, - bool barrier, TemporaryTypeSet* objTypes) -{ - MOZ_ASSERT(*emitted == false); - - if (barrier) { - trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier); - return true; - } - - JSValueType unboxedType; - uint32_t offset = getUnboxedOffset(obj->resultTypeSet(), name, &unboxedType); - if (offset == UINT32_MAX) - return true; - - if (obj->type() != MIRType::Object) { - MGuardObject* guard = MGuardObject::New(alloc(), obj); - current->add(guard); - obj = guard; - } - - MInstruction* store = storeUnboxedProperty(obj, offset, unboxedType, value); - - current->push(value); - - if (!resumeAfter(store)) - return false; - - *emitted = true; - return true; -} - bool IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, @@ -13146,15 +12725,12 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, } BaselineInspector::ReceiverVector receivers(alloc()); - BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); - if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) + if (!inspector->maybeInfoForPropertyOp(pc, receivers)) return false; if (!canInlinePropertyOpShapes(receivers)) return true; - obj = convertUnboxedObjects(obj, convertUnboxedGroups); - if (receivers.length() == 1) { if (!receivers[0].group) { // Monomorphic store to a native object. @@ -13174,46 +12750,6 @@ IonBuilder::setPropTryInlineAccess(bool* emitted, MDefinition* obj, return true; } - if (receivers[0].shape) { - // Monomorphic store to an unboxed object expando. - spew("Inlining monomorphic unboxed expando SETPROP"); - - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - obj = addUnboxedExpandoGuard(obj, /* hasExpando = */ true, Bailout_ShapeGuard); - - MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj); - current->add(expando); - - expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard); - - Shape* shape = receivers[0].shape->searchLinear(NameToId(name)); - MOZ_ASSERT(shape); - - bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name)); - if (!storeSlot(expando, shape, value, needsBarrier)) - return false; - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; - return true; - } - - // Monomorphic store to an unboxed object. - spew("Inlining monomorphic unboxed SETPROP"); - - ObjectGroup* group = receivers[0].group; - if (!objTypes->hasType(TypeSet::ObjectType(group))) - return true; - - obj = addGroupGuard(obj, group, Bailout_ShapeGuard); - - const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name); - storeUnboxedProperty(obj, property->offset, property->type, value); - - current->push(value); - - trackOptimizationOutcome(TrackedOutcome::Monomorphic); - *emitted = true; return true; } @@ -13884,7 +13420,7 @@ IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec) bool IonBuilder::jsop_in() { - MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* obj = current->pop(); MDefinition* id = current->pop(); bool emitted = false; @@ -13911,11 +13447,8 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) if (shouldAbortOnPreliminaryGroups(obj)) return true; - JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id); - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!ElementAccessIsDenseNative(constraints(), obj, id)) - return true; - } + if (!ElementAccessIsDenseNative(constraints(), obj, id)) + return true; if (ElementAccessHasExtraIndexedProperty(this, obj)) return true; @@ -13930,10 +13463,10 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) id = idInt32; // Get the elements vector. - MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC); + MElements* elements = MElements::New(alloc(), obj); current->add(elements); - MInstruction* initLength = initializedLength(obj, elements, unboxedType); + MInstruction* initLength = initializedLength(obj, elements); // If there are no holes, speculate the InArray check will not fail. if (!needsHoleCheck && !failedBoundsCheck_) { @@ -13943,8 +13476,7 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id) } // Check if id < initLength and elem[id] not a hole. - MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck, - unboxedType); + MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck); current->add(ins); current->push(ins); @@ -14240,19 +13772,6 @@ IonBuilder::addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bail return guard; } -MInstruction* -IonBuilder::addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind) -{ - MGuardUnboxedExpando* guard = MGuardUnboxedExpando::New(alloc(), obj, hasExpando, bailoutKind); - current->add(guard); - - // If a shape guard failed in the past, don't optimize group guards. - if (failedShapeGuard_) - guard->setNotMovable(); - - return guard; -} - MInstruction* IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers) @@ -14262,15 +13781,6 @@ IonBuilder::addGuardReceiverPolymorphic(MDefinition* obj, // Monomorphic guard on a native object. return addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard); } - - if (!receivers[0].shape) { - // Guard on an unboxed object that does not have an expando. - obj = addGroupGuard(obj, receivers[0].group, Bailout_ShapeGuard); - return addUnboxedExpandoGuard(obj, /* hasExpando = */ false, Bailout_ShapeGuard); - } - - // Monomorphic receiver guards are not yet supported when the receiver - // is an unboxed object with an expando. } MGuardReceiverPolymorphic* guard = MGuardReceiverPolymorphic::New(alloc(), obj); @@ -14644,32 +14154,24 @@ IonBuilder::constantInt(int32_t i) } MInstruction* -IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements, JSValueType unboxedType) +IonBuilder::initializedLength(MDefinition* obj, MDefinition* elements) { - MInstruction* res; - if (unboxedType != JSVAL_TYPE_MAGIC) - res = MUnboxedArrayInitializedLength::New(alloc(), obj); - else - res = MInitializedLength::New(alloc(), elements); + MInstruction* res = MInitializedLength::New(alloc(), elements); current->add(res); return res; } MInstruction* -IonBuilder::setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count) +IonBuilder::setInitializedLength(MDefinition* obj, size_t count) { MOZ_ASSERT(count); - MInstruction* res; - if (unboxedType != JSVAL_TYPE_MAGIC) { - res = MSetUnboxedArrayInitializedLength::New(alloc(), obj, constant(Int32Value(count))); - } else { - // MSetInitializedLength takes the index of the last element, rather - // than the count itself. - MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); - current->add(elements); - res = MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); - } + // MSetInitializedLength takes the index of the last element, rather + // than the count itself. + MInstruction* elements = MElements::New(alloc(), obj, /* unboxed = */ false); + current->add(elements); + MInstruction* res = + MSetInitializedLength::New(alloc(), elements, constant(Int32Value(count - 1))); current->add(res); return res; } diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f24ef30c8..dd40b4bd6 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -346,9 +346,8 @@ class IonBuilder MConstant* constant(const Value& v); MConstant* constantInt(int32_t i); - MInstruction* initializedLength(MDefinition* obj, MDefinition* elements, - JSValueType unboxedType); - MInstruction* setInitializedLength(MDefinition* obj, JSValueType unboxedType, size_t count); + MInstruction* initializedLength(MDefinition* obj, MDefinition* elements); + MInstruction* setInitializedLength(MDefinition* obj, size_t count); // Improve the type information at tests MOZ_MUST_USE bool improveTypesAtTest(MDefinition* ins, bool trueBranch, MTest* test); @@ -401,7 +400,6 @@ class IonBuilder MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length); MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind); MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind); - MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind); MInstruction* addSharedTypedArrayGuard(MDefinition* obj); MInstruction* @@ -441,8 +439,6 @@ class IonBuilder BarrierKind barrier, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); - MOZ_MUST_USE bool getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name, TemporaryTypeSet* types); MOZ_MUST_USE bool getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, @@ -475,9 +471,6 @@ class IonBuilder MOZ_MUST_USE bool setPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, bool barrier, TemporaryTypeSet* objTypes); - MOZ_MUST_USE bool setPropTryUnboxed(bool* emitted, MDefinition* obj, - PropertyName* name, MDefinition* value, - bool barrier, TemporaryTypeSet* objTypes); MOZ_MUST_USE bool setPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, MDefinition* value, bool barrier, TemporaryTypeSet* objTypes); @@ -617,7 +610,6 @@ class IonBuilder TypedObjectPrediction elemTypeReprs, uint32_t elemSize); MOZ_MUST_USE bool initializeArrayElement(MDefinition* obj, size_t index, MDefinition* value, - JSValueType unboxedType, bool addResumePointAndIncrementInitializedLength); // jsop_getelem() helpers. @@ -729,15 +721,13 @@ class IonBuilder MOZ_MUST_USE bool jsop_bindname(PropertyName* name); MOZ_MUST_USE bool jsop_bindvar(); MOZ_MUST_USE bool jsop_getelem(); - MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, - JSValueType unboxedType); + MOZ_MUST_USE bool jsop_getelem_dense(MDefinition* obj, MDefinition* index); MOZ_MUST_USE bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType); MOZ_MUST_USE bool jsop_setelem(); MOZ_MUST_USE bool jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion, MDefinition* object, MDefinition* index, - MDefinition* value, JSValueType unboxedType, - bool writeHole, bool* emitted); + MDefinition* value, bool writeHole, bool* emitted); MOZ_MUST_USE bool jsop_setelem_typed(ScalarTypeDescr::Type arrayType, MDefinition* object, MDefinition* index, MDefinition* value); @@ -1041,7 +1031,6 @@ class IonBuilder MDefinition* addShapeGuardsForGetterSetter(MDefinition* obj, JSObject* holder, Shape* holderShape, const BaselineInspector::ReceiverVector& receivers, - const BaselineInspector::ObjectGroupVector& convertUnboxedGroups, bool isOwnProperty); MOZ_MUST_USE bool annotateGetPropertyCache(MDefinition* obj, PropertyName* name, @@ -1059,22 +1048,6 @@ class IonBuilder ResultWithOOM testNotDefinedProperty(MDefinition* obj, jsid id); uint32_t getDefiniteSlot(TemporaryTypeSet* types, PropertyName* name, uint32_t* pnfixed); - MDefinition* convertUnboxedObjects(MDefinition* obj); - MDefinition* convertUnboxedObjects(MDefinition* obj, - const BaselineInspector::ObjectGroupVector& list); - uint32_t getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, - JSValueType* punboxedType); - MInstruction* loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types); - MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - BarrierKind barrier, TemporaryTypeSet* types); - MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType, - MDefinition* value); - MInstruction* storeUnboxedValue(MDefinition* obj, - MDefinition* elements, int32_t elementsOffset, - MDefinition* scaledOffset, JSValueType unboxedType, - MDefinition* value, bool preBarrier = true); MOZ_MUST_USE bool checkPreliminaryGroups(MDefinition *obj); MOZ_MUST_USE bool freezePropTypeSets(TemporaryTypeSet* types, JSObject* foundProto, PropertyName* name); diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 96e488ea8..9901bdd07 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -619,29 +619,7 @@ TestMatchingReceiver(MacroAssembler& masm, IonCache::StubAttacher& attacher, Register object, JSObject* obj, Label* failure, bool alwaysCheckGroup = false) { - if (obj->is()) { - MOZ_ASSERT(failure); - - masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label success; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, expando->lastProperty(), - &success); - masm.pop(object); - masm.jump(failure); - masm.bind(&success); - masm.pop(object); - } else { - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } - } else if (obj->is()) { - MOZ_ASSERT(failure); - masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure); - } else if (obj->is()) { + if (obj->is()) { attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, Address(object, JSObject::offsetOfGroup()), ImmGCPtr(obj->group()), failure); @@ -758,7 +736,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, // jump directly. Otherwise, jump to the end of the stub, so there's a // common point to patch. bool multipleFailureJumps = (obj != holder) - || obj->is() || (checkTDZ && output.hasValue()) || (failures != nullptr && failures->used()); @@ -777,7 +754,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, Register scratchReg = Register::FromCode(0); // Quell compiler warning. if (obj != holder || - obj->is() || !holder->as().isFixedSlot(shape->slot())) { if (output.hasValue()) { @@ -838,10 +814,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, holderReg = InvalidReg; } - } else if (obj->is()) { - holder = obj->as().maybeExpando(); - holderReg = scratchReg; - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), holderReg); } else { holderReg = object; } @@ -869,30 +841,6 @@ GenerateReadSlot(JSContext* cx, IonScript* ion, MacroAssembler& masm, attacher.jumpNextStub(masm); } -static void -GenerateReadUnboxed(JSContext* cx, IonScript* ion, MacroAssembler& masm, - IonCache::StubAttacher& attacher, JSObject* obj, - const UnboxedLayout::Property* property, - Register object, TypedOrValueRegister output, - Label* failures = nullptr) -{ - // Guard on the group of the object. - attacher.branchNextStubOrLabel(masm, Assembler::NotEqual, - Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(obj->group()), failures); - - Address address(object, UnboxedPlainObject::offsetOfData() + property->offset); - - masm.loadUnboxedProperty(address, property->type, output); - - attacher.jumpRejoin(masm); - - if (failures) { - masm.bind(failures); - attacher.jumpNextStub(masm); - } -} - static bool EmitGetterCall(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, JSObject* obj, @@ -1187,39 +1135,6 @@ GenerateArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& return true; } -static void -GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, - JSObject* array, Register object, TypedOrValueRegister output, - Label* failures) -{ - Register outReg; - if (output.hasValue()) { - outReg = output.valueReg().scratchReg(); - } else { - MOZ_ASSERT(output.type() == MIRType::Int32); - outReg = output.typedReg().gpr(); - } - MOZ_ASSERT(object != outReg); - - TestMatchingReceiver(masm, attacher, object, array, failures); - - // Load length. - masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg); - - // Check for a length that fits in an int32. - masm.branchTest32(Assembler::Signed, outReg, outReg, failures); - - if (output.hasValue()) - masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg()); - - // Success. - attacher.jumpRejoin(masm); - - // Failure. - masm.bind(failures); - attacher.jumpNextStub(masm); -} - // In this case, the code for TypedArray and SharedTypedArray is not the same, // because the code embeds pointers to the respective class arrays. Code that // caches the stub code must distinguish between the two cases. @@ -1532,101 +1447,6 @@ GetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip return linkAndAttachStub(cx, masm, attacher, ion, attachKind, outcome); } -bool -GetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is()) - return true; - const UnboxedLayout::Property* property = obj->as().layout().lookup(id); - if (!property) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - Label* maybeFailures = failures.used() ? &failures : nullptr; - - StubAttacher attacher(*this); - GenerateReadUnboxed(cx, ion, masm, attacher, obj, property, object(), output(), maybeFailures); - return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed", - JS::TrackedOutcome::ICGetPropStub_UnboxedRead); -} - -bool -GetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is()) - return true; - Rooted expando(cx, obj->as().maybeExpando()); - if (!expando) - return true; - - Shape* shape = expando->lookup(cx, id); - if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - Label* maybeFailures = failures.used() ? &failures : nullptr; - - StubAttacher attacher(*this); - GenerateReadSlot(cx, ion, masm, attacher, DontCheckTDZ, obj, obj, - shape, object(), output(), maybeFailures); - return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando", - JS::TrackedOutcome::ICGetPropStub_UnboxedReadExpando); -} - -bool -GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, - bool* emitted) -{ - MOZ_ASSERT(canAttachStub()); - MOZ_ASSERT(!*emitted); - MOZ_ASSERT(outerScript->ionScript() == ion); - - if (!obj->is()) - return true; - - if (!JSID_IS_ATOM(id, cx->names().length)) - return true; - - if (obj->as().length() > INT32_MAX) - return true; - - if (!allowArrayLength(cx)) - return true; - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - - Label failures; - emitIdGuard(masm, id, &failures); - - StubAttacher attacher(*this); - GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output(), &failures); - return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length", - JS::TrackedOutcome::ICGetPropStub_UnboxedArrayLength); -} - bool GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) @@ -2196,15 +2016,6 @@ GetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, returnAddr, emitted)) return false; - if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, id, returnAddr, emitted)) - return false; - if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, id, emitted)) return false; } @@ -2383,12 +2194,6 @@ GenerateSetSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att NativeObject::slotsSizeMustNotOverflow(); - if (obj->is()) { - obj = obj->as().maybeExpando(); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), tempReg); - object = tempReg; - } - if (obj->as().isFixedSlot(shape->slot())) { Address addr(object, NativeObject::getFixedSlotOffset(shape->slot())); @@ -3026,23 +2831,13 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.branchTestObjGroup(Assembler::NotEqual, object, oldGroup, failures); if (obj->maybeShape()) { masm.branchTestObjShape(Assembler::NotEqual, object, oldShape, failures); - } else { - MOZ_ASSERT(obj->is()); - - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failures); - - masm.loadPtr(expandoAddress, tempReg); - masm.branchTestObjShape(Assembler::NotEqual, tempReg, oldShape, failures); } Shape* newShape = obj->maybeShape(); - if (!newShape) - newShape = obj->as().maybeExpando()->lastProperty(); // Guard that the incoming value is in the type set for the property // if a type barrier is required. - if (checkTypeset) + if (newShape && checkTypeset) CheckTypeSetForWrite(masm, obj, newShape->propid(), tempReg, value, failures); // Guard shapes along prototype chain. @@ -3063,9 +2858,7 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att } // Call a stub to (re)allocate dynamic slots, if necessary. - uint32_t newNumDynamicSlots = obj->is() - ? obj->as().maybeExpando()->numDynamicSlots() - : obj->as().numDynamicSlots(); + uint32_t newNumDynamicSlots = obj->as().numDynamicSlots(); if (NativeObject::dynamicSlotsCount(oldShape) != newNumDynamicSlots) { AllocatableRegisterSet regs(RegisterSet::Volatile()); LiveRegisterSet save(regs.asLiveSet()); @@ -3076,12 +2869,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att Register temp1 = regs.takeAnyGeneral(); Register temp2 = regs.takeAnyGeneral(); - if (obj->is()) { - // Pass the expando object to the stub. - masm.Push(object); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); - } - masm.setupUnalignedABICall(temp1); masm.loadJSContext(temp1); masm.passABIArg(temp1); @@ -3098,27 +2885,16 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.jump(&allocDone); masm.bind(&allocFailed); - if (obj->is()) - masm.Pop(object); masm.PopRegsInMask(save); masm.jump(failures); masm.bind(&allocDone); masm.setFramePushed(framePushedAfterCall); - if (obj->is()) - masm.Pop(object); masm.PopRegsInMask(save); } bool popObject = false; - if (obj->is()) { - masm.push(object); - popObject = true; - obj = obj->as().maybeExpando(); - masm.loadPtr(Address(object, UnboxedPlainObject::offsetOfExpando()), object); - } - // Write the object or expando object's new shape. Address shapeAddr(object, ShapedObject::offsetOfShape()); if (cx->zone()->needsIncrementalBarrier()) @@ -3126,8 +2902,6 @@ GenerateAddSlot(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& att masm.storePtr(ImmGCPtr(newShape), shapeAddr); if (oldGroup != obj->group()) { - MOZ_ASSERT(!obj->is()); - // Changing object's group from a partially to fully initialized group, // per the acquired properties analysis. Only change the group if the // old group still has a newScript. @@ -3370,141 +3144,6 @@ CanAttachNativeSetProp(JSContext* cx, HandleObject obj, HandleId id, const Const return SetPropertyIC::CanAttachNone; } -static void -GenerateSetUnboxed(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, - JSObject* obj, jsid id, uint32_t unboxedOffset, JSValueType unboxedType, - Register object, Register tempReg, const ConstantOrRegister& value, - bool checkTypeset, Label* failures) -{ - // Guard on the type of the object. - masm.branchPtr(Assembler::NotEqual, - Address(object, JSObject::offsetOfGroup()), - ImmGCPtr(obj->group()), failures); - - if (checkTypeset) - CheckTypeSetForWrite(masm, obj, id, tempReg, value, failures); - - Address address(object, UnboxedPlainObject::offsetOfData() + unboxedOffset); - - if (cx->zone()->needsIncrementalBarrier()) { - if (unboxedType == JSVAL_TYPE_OBJECT) - masm.callPreBarrier(address, MIRType::Object); - else if (unboxedType == JSVAL_TYPE_STRING) - masm.callPreBarrier(address, MIRType::String); - else - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType)); - } - - masm.storeUnboxedProperty(address, unboxedType, value, failures); - - attacher.jumpRejoin(masm); - - masm.bind(failures); - attacher.jumpNextStub(masm); -} - -static bool -CanAttachSetUnboxed(JSContext* cx, HandleObject obj, HandleId id, const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset, - uint32_t* unboxedOffset, JSValueType* unboxedType) -{ - if (!obj->is()) - return false; - - const UnboxedLayout::Property* property = obj->as().layout().lookup(id); - if (property) { - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - *unboxedOffset = property->offset; - *unboxedType = property->type; - return true; - } - - return false; -} - -static bool -CanAttachSetUnboxedExpando(JSContext* cx, HandleObject obj, HandleId id, - const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset, Shape** pshape) -{ - if (!obj->is()) - return false; - - Rooted expando(cx, obj->as().maybeExpando()); - if (!expando) - return false; - - Shape* shape = expando->lookupPure(id); - if (!shape || !shape->hasDefaultSetter() || !shape->hasSlot() || !shape->writable()) - return false; - - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - - *pshape = shape; - return true; -} - -static bool -CanAttachAddUnboxedExpando(JSContext* cx, HandleObject obj, HandleShape oldShape, - HandleId id, const ConstantOrRegister& val, - bool needsTypeBarrier, bool* checkTypeset) -{ - if (!obj->is()) - return false; - - Rooted expando(cx, obj->as().maybeExpando()); - if (!expando || expando->inDictionaryMode()) - return false; - - Shape* newShape = expando->lastProperty(); - if (newShape->isEmptyShape() || newShape->propid() != id || newShape->previous() != oldShape) - return false; - - MOZ_ASSERT(newShape->hasDefaultSetter() && newShape->hasSlot() && newShape->writable()); - - if (PrototypeChainShadowsPropertyAdd(cx, obj, id)) - return false; - - *checkTypeset = false; - if (needsTypeBarrier && !CanInlineSetPropTypeCheck(obj, id, val, checkTypeset)) - return false; - - return true; -} - -bool -SetPropertyIC::tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, bool* emitted) -{ - MOZ_ASSERT(!*emitted); - - bool checkTypeset = false; - uint32_t unboxedOffset; - JSValueType unboxedType; - if (!CanAttachSetUnboxed(cx, obj, id, value(), needsTypeBarrier(), &checkTypeset, - &unboxedOffset, &unboxedType)) - { - return true; - } - - *emitted = true; - - MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_); - StubAttacher attacher(*this); - - Label failures; - emitIdGuard(masm, id, &failures); - - GenerateSetUnboxed(cx, masm, attacher, obj, id, unboxedOffset, unboxedType, - object(), temp(), value(), checkTypeset, &failures); - return linkAndAttachStub(cx, masm, attacher, ion, "set_unboxed", - JS::TrackedOutcome::ICSetPropStub_SetUnboxed); -} - bool SetPropertyIC::tryAttachProxy(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted) @@ -3585,26 +3224,6 @@ SetPropertyIC::tryAttachNative(JSContext* cx, HandleScript outerScript, IonScrip MOZ_CRASH("Unreachable"); } -bool -SetPropertyIC::tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, bool* emitted) -{ - MOZ_ASSERT(!*emitted); - - RootedShape shape(cx); - bool checkTypeset = false; - if (!CanAttachSetUnboxedExpando(cx, obj, id, value(), needsTypeBarrier(), - &checkTypeset, shape.address())) - { - return true; - } - - if (!attachSetSlot(cx, outerScript, ion, obj, shape, checkTypeset)) - return false; - *emitted = true; - return true; -} - bool SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleValue idval, HandleValue value, @@ -3630,12 +3249,6 @@ SetPropertyIC::tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* if (!*emitted && !tryAttachNative(cx, outerScript, ion, obj, id, emitted, tryNativeAddSlot)) return false; - - if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, id, emitted)) - return false; - - if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, id, emitted)) - return false; } if (idval.isInt32()) { @@ -3687,16 +3300,6 @@ SetPropertyIC::tryAttachAddSlot(JSContext* cx, HandleScript outerScript, IonScri return true; } - checkTypeset = false; - if (CanAttachAddUnboxedExpando(cx, obj, oldShape, id, value(), needsTypeBarrier(), - &checkTypeset)) - { - if (!attachAddSlot(cx, outerScript, ion, obj, id, oldShape, oldGroup, checkTypeset)) - return false; - *emitted = true; - return true; - } - return true; } @@ -3718,11 +3321,6 @@ SetPropertyIC::update(JSContext* cx, HandleScript outerScript, size_t cacheIndex return false; oldShape = obj->maybeShape(); - if (obj->is()) { - MOZ_ASSERT(!oldShape); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - oldShape = expando->lastProperty(); - } } RootedId id(cx); @@ -4025,7 +3623,7 @@ GetPropertyIC::tryAttachDenseElementHole(JSContext* cx, HandleScript outerScript GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval, TypedOrValueRegister output) { - if (!obj->is() && !obj->is()) + if (!obj->is()) return false; MOZ_ASSERT(idval.isInt32() || idval.isString()); @@ -4056,13 +3654,6 @@ GetPropertyIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& i return output.hasValue() || !output.typedReg().isFloat(); } - if (index >= obj->as().initializedLength()) - return false; - - JSValueType elementType = obj->as().elementType(); - if (elementType == JSVAL_TYPE_DOUBLE) - return output.hasValue(); - return output.hasValue() || !output.typedReg().isFloat(); } @@ -4139,46 +3730,27 @@ GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm, Label popObjectAndFail; - if (array->is()) { - // Guard on the initialized length. - Address length(object, TypedArrayObject::lengthOffset()); - masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures); + // Guard on the initialized length. + Address length(object, TypedArrayObject::lengthOffset()); + masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures); - // Save the object register on the stack in case of failure. - Register elementReg = object; - masm.push(object); + // Save the object register on the stack in case of failure. + Register elementReg = object; + masm.push(object); - // Load elements vector. - masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg); + // Load elements vector. + masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg); - // Load the value. We use an invalid register because the destination - // register is necessary a non double register. - Scalar::Type arrayType = array->as().type(); - int width = Scalar::byteSize(arrayType); - BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width)); - if (output.hasValue()) { - masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult, - elementReg, &popObjectAndFail); - } else { - masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail); - } + // Load the value. We use an invalid register because the destination + // register is necessary a non double register. + Scalar::Type arrayType = array->as().type(); + int width = Scalar::byteSize(arrayType); + BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width)); + if (output.hasValue()) { + masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult, + elementReg, &popObjectAndFail); } else { - // Save the object register on the stack in case of failure. - masm.push(object); - - // Guard on the initialized length. - masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object); - masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object); - masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail); - - // Load elements vector. - Register elementReg = object; - masm.loadPtr(Address(masm.getStackPointer(), 0), object); - masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg); - - JSValueType elementType = array->as().elementType(); - BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType))); - masm.loadUnboxedProperty(source, elementType, output); + masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail); } masm.pop(object); diff --git a/js/src/jit/IonCaches.h b/js/src/jit/IonCaches.h index 173e06c6b..b00646538 100644 --- a/js/src/jit/IonCaches.h +++ b/js/src/jit/IonCaches.h @@ -529,18 +529,6 @@ class GetPropertyIC : public IonCache HandleObject obj, HandleId id, void* returnAddr, bool* emitted); - MOZ_MUST_USE bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion, - HandleObject obj, HandleId id, void* returnAddr, - bool* emitted); - - MOZ_MUST_USE bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, - IonScript* ion, HandleObject obj, HandleId id, - void* returnAddr, bool* emitted); - - MOZ_MUST_USE bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, - IonScript* ion, HandleObject obj, HandleId id, - void* returnAddr, bool* emitted); - MOZ_MUST_USE bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion, HandleObject obj, HandleId id, bool* emitted); diff --git a/js/src/jit/JitOptions.cpp b/js/src/jit/JitOptions.cpp index b9a7c7b27..3f9d9db88 100644 --- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -221,9 +221,6 @@ DefaultJitOptions::DefaultJitOptions() Warn(forcedRegisterAllocatorEnv, env); } - // Toggles whether unboxed plain objects can be created by the VM. - SET_DEFAULT(disableUnboxedObjects, true); - // Test whether Atomics are allowed in asm.js code. SET_DEFAULT(asmJSAtomicsEnable, false); diff --git a/js/src/jit/JitOptions.h b/js/src/jit/JitOptions.h index 076980b4e..719ee14d9 100644 --- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -91,9 +91,6 @@ struct DefaultJitOptions mozilla::Maybe forcedDefaultIonSmallFunctionWarmUpThreshold; mozilla::Maybe forcedRegisterAllocator; - // The options below affect the rest of the VM, and not just the JIT. - bool disableUnboxedObjects; - DefaultJitOptions(); bool isSmallFunction(JSScript* script) const; void setEagerCompilation(); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 709de9987..68417138b 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2888,32 +2888,6 @@ LIRGenerator::visitSetInitializedLength(MSetInitializedLength* ins) useRegisterOrConstant(ins->index())), ins); } -void -LIRGenerator::visitUnboxedArrayLength(MUnboxedArrayLength* ins) -{ - define(new(alloc()) LUnboxedArrayLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins) -{ - define(new(alloc()) LUnboxedArrayInitializedLength(useRegisterAtStart(ins->object())), ins); -} - -void -LIRGenerator::visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins) -{ - add(new(alloc()) LIncrementUnboxedArrayInitializedLength(useRegister(ins->object())), ins); -} - -void -LIRGenerator::visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins) -{ - add(new(alloc()) LSetUnboxedArrayInitializedLength(useRegister(ins->object()), - useRegisterOrConstant(ins->length()), - temp()), ins); -} - void LIRGenerator::visitNot(MNot* ins) { @@ -3163,22 +3137,16 @@ LIRGenerator::visitStoreElementHole(MStoreElementHole* ins) const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); - // Use a temp register when adding new elements to unboxed arrays. - LDefinition tempDef = LDefinition::BogusTemp(); - if (ins->unboxedType() != JSVAL_TYPE_MAGIC) - tempDef = temp(); - LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value()), - tempDef); + lir = new(alloc()) LStoreElementHoleV(object, elements, index, useBox(ins->value())); break; default: { const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LStoreElementHoleT(object, elements, index, value, tempDef); + lir = new(alloc()) LStoreElementHoleT(object, elements, index, value); break; } } @@ -3197,20 +3165,14 @@ LIRGenerator::visitFallibleStoreElement(MFallibleStoreElement* ins) const LUse elements = useRegister(ins->elements()); const LAllocation index = useRegisterOrConstant(ins->index()); - // Use a temp register when adding new elements to unboxed arrays. - LDefinition tempDef = LDefinition::BogusTemp(); - if (ins->unboxedType() != JSVAL_TYPE_MAGIC) - tempDef = temp(); - LInstruction* lir; switch (ins->value()->type()) { case MIRType::Value: - lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value()), - tempDef); + lir = new(alloc()) LFallibleStoreElementV(object, elements, index, useBox(ins->value())); break; default: const LAllocation value = useRegisterOrNonDoubleConstant(ins->value()); - lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value, tempDef); + lir = new(alloc()) LFallibleStoreElementT(object, elements, index, value); break; } @@ -3251,14 +3213,6 @@ LIRGenerator::visitStoreUnboxedString(MStoreUnboxedString* ins) add(lir, ins); } -void -LIRGenerator::visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins) -{ - LInstruction* check = new(alloc()) LConvertUnboxedObjectToNative(useRegister(ins->object())); - add(check, ins); - assignSafepoint(check, ins); -} - void LIRGenerator::visitEffectiveAddress(MEffectiveAddress* ins) { @@ -3776,24 +3730,6 @@ LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins) redefine(ins, ins->object()); } -void -LIRGenerator::visitGuardUnboxedExpando(MGuardUnboxedExpando* ins) -{ - LGuardUnboxedExpando* guard = - new(alloc()) LGuardUnboxedExpando(useRegister(ins->object())); - assignSnapshot(guard, ins->bailoutKind()); - add(guard, ins); - redefine(ins, ins->object()); -} - -void -LIRGenerator::visitLoadUnboxedExpando(MLoadUnboxedExpando* ins) -{ - LLoadUnboxedExpando* lir = - new(alloc()) LLoadUnboxedExpando(useRegisterAtStart(ins->object())); - define(lir, ins); -} - void LIRGenerator::visitAssertRange(MAssertRange* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 9b4095aec..b096bb143 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -216,10 +216,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitTypedObjectDescr(MTypedObjectDescr* ins); void visitInitializedLength(MInitializedLength* ins); void visitSetInitializedLength(MSetInitializedLength* ins); - void visitUnboxedArrayLength(MUnboxedArrayLength* ins); - void visitUnboxedArrayInitializedLength(MUnboxedArrayInitializedLength* ins); - void visitIncrementUnboxedArrayInitializedLength(MIncrementUnboxedArrayInitializedLength* ins); - void visitSetUnboxedArrayInitializedLength(MSetUnboxedArrayInitializedLength* ins); void visitNot(MNot* ins); void visitBoundsCheck(MBoundsCheck* ins); void visitBoundsCheckLower(MBoundsCheckLower* ins); @@ -232,7 +228,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitFallibleStoreElement(MFallibleStoreElement* ins); void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins); void visitStoreUnboxedString(MStoreUnboxedString* ins); - void visitConvertUnboxedObjectToNative(MConvertUnboxedObjectToNative* ins); void visitEffectiveAddress(MEffectiveAddress* ins); void visitArrayPopShift(MArrayPopShift* ins); void visitArrayPush(MArrayPush* ins); @@ -256,8 +251,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGuardObject(MGuardObject* ins); void visitGuardString(MGuardString* ins); void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic* ins); - void visitGuardUnboxedExpando(MGuardUnboxedExpando* ins); - void visitLoadUnboxedExpando(MLoadUnboxedExpando* ins); void visitPolyInlineGuard(MPolyInlineGuard* ins); void visitAssertRange(MAssertRange* ins); void visitCallGetProperty(MCallGetProperty* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 01755094a..b9c7c409d 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -30,7 +30,6 @@ #include "jit/shared/Lowering-shared-inl.h" #include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" -#include "vm/UnboxedObject-inl.h" using mozilla::ArrayLength; using mozilla::AssertedCast; @@ -475,11 +474,6 @@ IonBuilder::inlineArray(CallInfo& callInfo) return InliningStatus_NotInlined; } - if (templateObject->is()) { - if (templateObject->group()->unboxedLayout().nativeGroup()) - return InliningStatus_NotInlined; - } - // Multiple arguments imply array initialization, not just construction. if (callInfo.argc() >= 2) { initLength = callInfo.argc(); @@ -527,7 +521,7 @@ IonBuilder::inlineArray(CallInfo& callInfo) // Make sure initLength matches the template object's length. This is // not guaranteed to be the case, for instance if we're inlining the // MConstant may come from an outer script. - if (initLength != GetAnyBoxedOrUnboxedArrayLength(templateObject)) + if (initLength != templateObject->as().length()) return InliningStatus_NotInlined; // Don't inline large allocations. @@ -542,16 +536,15 @@ IonBuilder::inlineArray(CallInfo& callInfo) MDefinition* array = current->peek(-1); if (callInfo.argc() >= 2) { - JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); for (uint32_t i = 0; i < initLength; i++) { if (!alloc().ensureBallast()) return InliningStatus_Error; MDefinition* value = callInfo.getArg(i); - if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false)) + if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false)) return InliningStatus_Error; } - MInstruction* setLength = setInitializedLength(array, unboxedType, initLength); + MInstruction* setLength = setInitializedLength(array, initLength); if (!resumeAfter(setLength)) return InliningStatus_Error; } @@ -584,7 +577,7 @@ IonBuilder::inlineArrayIsArray(CallInfo& callInfo) if (!clasp || clasp->isProxy()) return InliningStatus_NotInlined; - isArray = (clasp == &ArrayObject::class_ || clasp == &UnboxedArrayObject::class_); + isArray = (clasp == &ArrayObject::class_); } pushConstant(BooleanValue(isArray)); @@ -615,12 +608,12 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) OBJECT_FLAG_LENGTH_OVERFLOW | OBJECT_FLAG_ITERATED; - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); TemporaryTypeSet* thisTypes = obj->resultTypeSet(); if (!thisTypes) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) { trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags); @@ -632,17 +625,9 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) return InliningStatus_NotInlined; } - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - callInfo.setImplicitlyUsedUnchecked(); - if (clasp == &ArrayObject::class_) - obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); + obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); TemporaryTypeSet* returnTypes = getInlineReturnTypeSet(); bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED); @@ -653,8 +638,7 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode) if (barrier != BarrierKind::NoBarrier) returnType = MIRType::Value; - MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, - unboxedType, needsHoleCheck, maybeUndefined); + MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined); current->add(ins); current->push(ins); ins->setResultType(returnType); @@ -743,7 +727,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); MDefinition* value = callInfo.getArg(0); if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, nullptr, &value, /* canModify = */ false)) @@ -761,7 +745,7 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) if (!thisTypes) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW)) @@ -782,13 +766,6 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) return InliningStatus_NotInlined; } - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - callInfo.setImplicitlyUsedUnchecked(); if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles || @@ -799,13 +776,12 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo) value = valueDouble; } - if (unboxedType == JSVAL_TYPE_MAGIC) - obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); + obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false); if (NeedsPostBarrier(value)) current->add(MPostWriteBarrier::New(alloc(), obj, value)); - MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType); + MArrayPush* ins = MArrayPush::New(alloc(), obj, value); current->add(ins); current->push(ins); @@ -822,7 +798,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; } - MDefinition* obj = convertUnboxedObjects(callInfo.thisArg()); + MDefinition* obj = callInfo.thisArg(); // Ensure |this| and result are objects. if (getInlineReturnType() != MIRType::Object) @@ -846,7 +822,7 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; const Class* clasp = thisTypes->getKnownClass(constraints()); - if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) + if (clasp != &ArrayObject::class_) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES | OBJECT_FLAG_LENGTH_OVERFLOW)) @@ -855,13 +831,6 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) return InliningStatus_NotInlined; } - JSValueType unboxedType = JSVAL_TYPE_MAGIC; - if (clasp == &UnboxedArrayObject::class_) { - unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr); - if (unboxedType == JSVAL_TYPE_MAGIC) - return InliningStatus_NotInlined; - } - // Watch out for indexed properties on the prototype. if (ArrayPrototypeHasIndexedProperty(this, script())) { trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps); @@ -882,15 +851,8 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) if (!templateObj) return InliningStatus_NotInlined; - if (unboxedType == JSVAL_TYPE_MAGIC) { - if (!templateObj->is()) - return InliningStatus_NotInlined; - } else { - if (!templateObj->is()) - return InliningStatus_NotInlined; - if (templateObj->as().elementType() != unboxedType) - return InliningStatus_NotInlined; - } + if (!templateObj->is()) + return InliningStatus_NotInlined; callInfo.setImplicitlyUsedUnchecked(); @@ -909,16 +871,12 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo) end = MArrayLength::New(alloc(), elements); current->add(end->toInstruction()); - } else { - end = MUnboxedArrayLength::New(alloc(), obj); - current->add(end->toInstruction()); } MArraySlice* ins = MArraySlice::New(alloc(), constraints(), obj, begin, end, templateObj, - templateObj->group()->initialHeap(constraints()), - unboxedType); + templateObj->group()->initialHeap(constraints())); current->add(ins); current->push(ins); @@ -1437,7 +1395,7 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) // Check if exist a template object in stub. JSString* stringStr = nullptr; JSString* stringSep = nullptr; - JSObject* templateObject = nullptr; + ArrayObject* templateObject = nullptr; if (!inspector->isOptimizableCallStringSplit(pc, &stringStr, &stringSep, &templateObject)) return InliningStatus_NotInlined; @@ -1463,13 +1421,13 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) if (!key.maybeTypes()->hasType(TypeSet::StringType())) return InliningStatus_NotInlined; - uint32_t initLength = GetAnyBoxedOrUnboxedArrayLength(templateObject); - if (GetAnyBoxedOrUnboxedInitializedLength(templateObject) != initLength) + uint32_t initLength = templateObject->length(); + if (templateObject->getDenseInitializedLength() != initLength) return InliningStatus_NotInlined; Vector arrayValues; for (uint32_t i = 0; i < initLength; i++) { - Value str = GetAnyBoxedOrUnboxedDenseElement(templateObject, i); + Value str = templateObject->getDenseElement(i); MOZ_ASSERT(str.toString()->isAtom()); MConstant* value = MConstant::New(alloc().fallible(), str, constraints()); if (!value) @@ -1500,8 +1458,6 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) return InliningStatus_Inlined; } - JSValueType unboxedType = GetBoxedOrUnboxedType(templateObject); - // Store all values, no need to initialize the length after each as // jsop_initelem_array is doing because we do not expect to bailout // because the memory is supposed to be allocated by now. @@ -1512,11 +1468,11 @@ IonBuilder::inlineConstantStringSplitString(CallInfo& callInfo) MConstant* value = arrayValues[i]; current->add(value); - if (!initializeArrayElement(array, i, value, unboxedType, /* addResumePoint = */ false)) + if (!initializeArrayElement(array, i, value, /* addResumePoint = */ false)) return InliningStatus_Error; } - MInstruction* setLength = setInitializedLength(array, unboxedType, initLength); + MInstruction* setLength = setInitializedLength(array, initLength); if (!resumeAfter(setLength)) return InliningStatus_Error; @@ -2152,7 +2108,7 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo) if (callInfo.argc() != 3) return InliningStatus_NotInlined; - MDefinition* obj = convertUnboxedObjects(callInfo.getArg(0)); + MDefinition* obj = callInfo.getArg(0); MDefinition* id = callInfo.getArg(1); MDefinition* value = callInfo.getArg(2); diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 287b87582..6aa46be4b 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -4810,35 +4810,8 @@ MBeta::printOpcode(GenericPrinter& out) const bool MCreateThisWithTemplate::canRecoverOnBailout() const { - MOZ_ASSERT(templateObject()->is() || templateObject()->is()); - MOZ_ASSERT_IF(templateObject()->is(), - !templateObject()->as().denseElementsAreCopyOnWrite()); - return true; -} - -bool -OperandIndexMap::init(TempAllocator& alloc, JSObject* templateObject) -{ - const UnboxedLayout& layout = - templateObject->as().layoutDontCheckGeneration(); - - const UnboxedLayout::PropertyVector& properties = layout.properties(); - MOZ_ASSERT(properties.length() < 255); - - // Allocate an array of indexes, where the top of each field correspond to - // the index of the operand in the MObjectState instance. - if (!map.init(alloc, layout.size())) - return false; - - // Reset all indexes to 0, which is an error code. - for (size_t i = 0; i < map.length(); i++) - map[i] = 0; - - // Map the property offsets to the indexes of MObjectState operands. - uint8_t index = 1; - for (size_t i = 0; i < properties.length(); i++, index++) - map[properties[i].offset] = index; - + MOZ_ASSERT(templateObject()->is()); + MOZ_ASSERT(!templateObject()->as().denseElementsAreCopyOnWrite()); return true; } @@ -4858,17 +4831,11 @@ MObjectState::MObjectState(JSObject *templateObject, OperandIndexMap* operandInd setResultType(MIRType::Object); setRecoveredOnBailout(); - if (templateObject->is()) { - NativeObject* nativeObject = &templateObject->as(); - numSlots_ = nativeObject->slotSpan(); - numFixedSlots_ = nativeObject->numFixedSlots(); - } else { - const UnboxedLayout& layout = - templateObject->as().layoutDontCheckGeneration(); - // Same as UnboxedLayout::makeNativeGroup - numSlots_ = layout.properties().length(); - numFixedSlots_ = gc::GetGCKindSlots(layout.getAllocKind()); - } + MOZ_ASSERT(templateObject->is()); + + NativeObject* nativeObject = &templateObject->as(); + numSlots_ = nativeObject->slotSpan(); + numFixedSlots_ = nativeObject->numFixedSlots(); operandIndex_ = operandIndex; } @@ -4905,39 +4872,21 @@ MObjectState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefine // the template object. This is needed to account values which are baked in // the template objects and not visible in IonMonkey, such as the // uninitialized-lexical magic value of call objects. - if (templateObject->is()) { - UnboxedPlainObject& unboxedObject = templateObject->as(); - const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration(); - const UnboxedLayout::PropertyVector& properties = layout.properties(); + NativeObject& nativeObject = templateObject->as(); + MOZ_ASSERT(nativeObject.slotSpan() == numSlots()); - for (size_t i = 0; i < properties.length(); i++) { - Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true); - MDefinition *def = undefinedVal; - if (!val.isUndefined()) { - MConstant* ins = val.isObject() ? - MConstant::NewConstraintlessObject(alloc, &val.toObject()) : - MConstant::New(alloc, val); - block()->insertBefore(this, ins); - def = ins; - } - initSlot(i, def); - } - } else { - NativeObject& nativeObject = templateObject->as(); - MOZ_ASSERT(nativeObject.slotSpan() == numSlots()); - - for (size_t i = 0; i < numSlots(); i++) { - Value val = nativeObject.getSlot(i); - MDefinition *def = undefinedVal; - if (!val.isUndefined()) { - MConstant* ins = val.isObject() ? - MConstant::NewConstraintlessObject(alloc, &val.toObject()) : - MConstant::New(alloc, val); - block()->insertBefore(this, ins); - def = ins; - } - initSlot(i, def); + MOZ_ASSERT(templateObject->is()); + for (size_t i = 0; i < numSlots(); i++) { + Value val = nativeObject.getSlot(i); + MDefinition *def = undefinedVal; + if (!val.isUndefined()) { + MConstant* ins = val.isObject() ? + MConstant::NewConstraintlessObject(alloc, &val.toObject()) : + MConstant::New(alloc, val); + block()->insertBefore(this, ins); + def = ins; } + initSlot(i, def); } return true; } @@ -4948,14 +4897,7 @@ MObjectState::New(TempAllocator& alloc, MDefinition* obj) JSObject* templateObject = templateObjectOf(obj); MOZ_ASSERT(templateObject, "Unexpected object creation."); - OperandIndexMap* operandIndex = nullptr; - if (templateObject->is()) { - operandIndex = new(alloc) OperandIndexMap; - if (!operandIndex || !operandIndex->init(alloc, templateObject)) - return nullptr; - } - - MObjectState* res = new(alloc) MObjectState(templateObject, operandIndex); + MObjectState* res = new(alloc) MObjectState(templateObject, nullptr); if (!res || !res->init(alloc, obj)) return nullptr; return res; @@ -5862,35 +5804,6 @@ MGetFirstDollarIndex::foldsTo(TempAllocator& alloc) return MConstant::New(alloc, Int32Value(index)); } -MConvertUnboxedObjectToNative* -MConvertUnboxedObjectToNative::New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group) -{ - MConvertUnboxedObjectToNative* res = new(alloc) MConvertUnboxedObjectToNative(obj, group); - - ObjectGroup* nativeGroup = group->unboxedLayout().nativeGroup(); - - // Make a new type set for the result of this instruction which replaces - // the input group with the native group we will convert it to. - TemporaryTypeSet* types = obj->resultTypeSet(); - if (types && !types->unknownObject()) { - TemporaryTypeSet* newTypes = types->cloneWithoutObjects(alloc.lifoAlloc()); - if (newTypes) { - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - if (key->unknownProperties() || !key->isGroup() || key->group() != group) - newTypes->addType(TypeSet::ObjectType(key), alloc.lifoAlloc()); - else - newTypes->addType(TypeSet::ObjectType(nativeGroup), alloc.lifoAlloc()); - } - res->setResultTypeSet(newTypes); - } - } - - return res; -} - bool jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id) @@ -5910,48 +5823,6 @@ jit::ElementAccessIsDenseNative(CompilerConstraintList* constraints, return clasp && clasp->isNative() && !IsTypedArrayClass(clasp); } -JSValueType -jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id) -{ - if (obj->mightBeType(MIRType::String)) - return JSVAL_TYPE_MAGIC; - - if (id && id->type() != MIRType::Int32 && id->type() != MIRType::Double) - return JSVAL_TYPE_MAGIC; - - TemporaryTypeSet* types = obj->resultTypeSet(); - if (!types || types->unknownObject()) - return JSVAL_TYPE_MAGIC; - - JSValueType elementType = JSVAL_TYPE_MAGIC; - for (unsigned i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (!key) - continue; - - if (key->unknownProperties() || !key->isGroup()) - return JSVAL_TYPE_MAGIC; - - if (key->clasp() != &UnboxedArrayObject::class_) - return JSVAL_TYPE_MAGIC; - - const UnboxedLayout &layout = key->group()->unboxedLayout(); - - if (layout.nativeGroup()) - return JSVAL_TYPE_MAGIC; - - if (elementType == layout.elementType() || elementType == JSVAL_TYPE_MAGIC) - elementType = layout.elementType(); - else - return JSVAL_TYPE_MAGIC; - - key->watchStateChangeForUnboxedConvertedToNative(constraints); - } - - return elementType; -} - bool jit::ElementAccessIsTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, @@ -6111,11 +5982,6 @@ ObjectSubsumes(TypeSet::ObjectKey* first, TypeSet::ObjectKey* second) firstElements.maybeTypes()->equals(secondElements.maybeTypes()); } - if (first->clasp() == &UnboxedArrayObject::class_) { - return first->group()->unboxedLayout().elementType() == - second->group()->unboxedLayout().elementType(); - } - return false; } @@ -6579,23 +6445,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* } } - // Perform additional filtering to make sure that any unboxed property - // being written can accommodate the value. - for (size_t i = 0; i < types->getObjectCount(); i++) { - TypeSet::ObjectKey* key = types->getObject(i); - if (key && key->isGroup() && key->group()->maybeUnboxedLayout()) { - const UnboxedLayout& layout = key->group()->unboxedLayout(); - if (name) { - const UnboxedLayout::Property* property = layout.lookup(name); - if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue)) - return true; - } else { - if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue)) - return true; - } - } - } - if (success) return false; @@ -6626,17 +6475,6 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* MOZ_ASSERT(excluded); - // If the excluded object is a group with an unboxed layout, make sure it - // does not have a corresponding native group. Objects with the native - // group might appear even though they are not in the type set. - if (excluded->isGroup()) { - if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) { - if (layout->nativeGroup()) - return true; - excluded->watchStateChangeForUnboxedConvertedToNative(constraints); - } - } - *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true); return false; } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index fb0f22fc3..40d1145e0 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -30,7 +30,6 @@ #include "vm/EnvironmentObject.h" #include "vm/SharedMem.h" #include "vm/TypedArrayCommon.h" -#include "vm/UnboxedObject.h" // Undo windows.h damage on Win64 #undef MemoryBarrier @@ -376,8 +375,7 @@ class AliasSet { Element = 1 << 1, // A Value member of obj->elements or // a typed object. UnboxedElement = 1 << 2, // An unboxed scalar or reference member of - // a typed array, typed object, or unboxed - // object. + // typed object or unboxed object. DynamicSlot = 1 << 3, // A Value member of obj->slots. FixedSlot = 1 << 4, // A Value member of obj->fixedSlots(). DOMProperty = 1 << 5, // A DOM property @@ -434,9 +432,6 @@ class AliasSet { MOZ_ASSERT(flags && !(flags & Store_)); return AliasSet(flags | Store_); } - static uint32_t BoxedOrUnboxedElements(JSValueType type) { - return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement; - } }; typedef Vector MDefinitionVector; @@ -3795,7 +3790,6 @@ class MObjectState MOZ_MUST_USE bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal); size_t numFixedSlots() const { - MOZ_ASSERT(!isUnboxed()); return numFixedSlots_; } size_t numSlots() const { @@ -8746,102 +8740,6 @@ class MSetInitializedLength ALLOW_CLONE(MSetInitializedLength) }; -// Load the length from an unboxed array. -class MUnboxedArrayLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayLength) -}; - -// Load the initialized length from an unboxed array. -class MUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MUnboxedArrayInitializedLength(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Int32); - setMovable(); - } - - public: - INSTRUCTION_HEADER(UnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MUnboxedArrayInitializedLength) -}; - -// Increment the initialized length of an unboxed array object. -class MIncrementUnboxedArrayInitializedLength - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MIncrementUnboxedArrayInitializedLength(MDefinition* obj) - : MUnaryInstruction(obj) - {} - - public: - INSTRUCTION_HEADER(IncrementUnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MIncrementUnboxedArrayInitializedLength) -}; - -// Set the initialized length of an unboxed array object. -class MSetUnboxedArrayInitializedLength - : public MBinaryInstruction, - public SingleObjectPolicy::Data -{ - explicit MSetUnboxedArrayInitializedLength(MDefinition* obj, MDefinition* length) - : MBinaryInstruction(obj, length) - {} - - public: - INSTRUCTION_HEADER(SetUnboxedArrayInitializedLength) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object), (1, length)) - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields); - } - - ALLOW_CLONE(MSetUnboxedArrayInitializedLength) -}; - // Load the array length from an elements header. class MArrayLength : public MUnaryInstruction, @@ -9335,23 +9233,19 @@ class MLoadElement ALLOW_CLONE(MLoadElement) }; -// Load a value from the elements vector for a dense native or unboxed array. +// Load a value from the elements vector of a native object. // If the index is out-of-bounds, or the indexed slot has a hole, undefined is // returned instead. class MLoadElementHole : public MTernaryInstruction, public SingleObjectPolicy::Data { - // Unboxed element type, JSVAL_TYPE_MAGIC for dense native elements. - JSValueType unboxedType_; - bool needsNegativeIntCheck_; bool needsHoleCheck_; MLoadElementHole(MDefinition* elements, MDefinition* index, MDefinition* initLength, - JSValueType unboxedType, bool needsHoleCheck) + bool needsHoleCheck) : MTernaryInstruction(elements, index, initLength), - unboxedType_(unboxedType), needsNegativeIntCheck_(true), needsHoleCheck_(needsHoleCheck) { @@ -9373,9 +9267,6 @@ class MLoadElementHole TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, elements), (1, index), (2, initLength)) - JSValueType unboxedType() const { - return unboxedType_; - } bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } @@ -9386,8 +9277,6 @@ class MLoadElementHole if (!ins->isLoadElementHole()) return false; const MLoadElementHole* other = ins->toLoadElementHole(); - if (unboxedType() != other->unboxedType()) - return false; if (needsHoleCheck() != other->needsHoleCheck()) return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) @@ -9395,7 +9284,7 @@ class MLoadElementHole return congruentIfOperandsEqual(other); } AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Load(AliasSet::Element); } void collectRangeInfoPreTrunc() override; @@ -9575,20 +9464,17 @@ class MStoreElement ALLOW_CLONE(MStoreElement) }; -// Like MStoreElement, but supports indexes >= initialized length, and can -// handle unboxed arrays. The downside is that we cannot hoist the elements -// vector and bounds check, since this instruction may update the (initialized) -// length and reallocate the elements vector. +// Like MStoreElement, but supports indexes >= initialized length. The downside +// is that we cannot hoist the elements vector and bounds check, since this +// instruction may update the (initialized) length and reallocate the elements +// vector. class MStoreElementHole : public MAryInstruction<4>, public MStoreElementCommon, public MixPolicy >::Data { - JSValueType unboxedType_; - MStoreElementHole(MDefinition* object, MDefinition* elements, - MDefinition* index, MDefinition* value, JSValueType unboxedType) - : unboxedType_(unboxedType) + MDefinition* index, MDefinition* value) { initOperand(0, object); initOperand(1, elements); @@ -9603,14 +9489,10 @@ class MStoreElementHole TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { // StoreElementHole can update the initialized length, the array length // or reallocate obj->elements. - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } ALLOW_CLONE(MStoreElementHole) @@ -9623,13 +9505,11 @@ class MFallibleStoreElement public MStoreElementCommon, public MixPolicy >::Data { - JSValueType unboxedType_; bool strict_; MFallibleStoreElement(MDefinition* object, MDefinition* elements, MDefinition* index, MDefinition* value, - JSValueType unboxedType, bool strict) - : unboxedType_(unboxedType) + bool strict) { initOperand(0, object); initOperand(1, elements); @@ -9645,12 +9525,8 @@ class MFallibleStoreElement TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } bool strict() const { return strict_; @@ -9742,59 +9618,6 @@ class MStoreUnboxedString ALLOW_CLONE(MStoreUnboxedString) }; -// Passes through an object, after ensuring it is converted from an unboxed -// object to a native representation. -class MConvertUnboxedObjectToNative - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - CompilerObjectGroup group_; - - explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group) - : MUnaryInstruction(obj), - group_(group) - { - setGuard(); - setMovable(); - setResultType(MIRType::Object); - } - - public: - INSTRUCTION_HEADER(ConvertUnboxedObjectToNative) - NAMED_OPERANDS((0, object)) - - static MConvertUnboxedObjectToNative* New(TempAllocator& alloc, MDefinition* obj, - ObjectGroup* group); - - ObjectGroup* group() const { - return group_; - } - bool congruentTo(const MDefinition* ins) const override { - if (!congruentIfOperandsEqual(ins)) - return false; - return ins->toConvertUnboxedObjectToNative()->group() == group(); - } - AliasSet getAliasSet() const override { - // This instruction can read and write to all parts of the object, but - // is marked as non-effectful so it can be consolidated by LICM and GVN - // and avoid inhibiting other optimizations. - // - // This is valid to do because when unboxed objects might have a native - // group they can be converted to, we do not optimize accesses to the - // unboxed objects and do not guard on their group or shape (other than - // in this opcode). - // - // Later accesses can assume the object has a native representation - // and optimize accordingly. Those accesses cannot be reordered before - // this instruction, however. This is prevented by chaining this - // instruction with the object itself, in the same way as MBoundsCheck. - return AliasSet::None(); - } - bool appendRoots(MRootList& roots) const override { - return roots.append(group_); - } -}; - // Array.prototype.pop or Array.prototype.shift on a dense array. class MArrayPopShift : public MUnaryInstruction, @@ -9808,13 +9631,12 @@ class MArrayPopShift private: Mode mode_; - JSValueType unboxedType_; bool needsHoleCheck_; bool maybeUndefined_; - MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType, + MArrayPopShift(MDefinition* object, Mode mode, bool needsHoleCheck, bool maybeUndefined) - : MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType), + : MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined) { } @@ -9832,12 +9654,8 @@ class MArrayPopShift bool mode() const { return mode_; } - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } ALLOW_CLONE(MArrayPopShift) @@ -9848,10 +9666,8 @@ class MArrayPush : public MBinaryInstruction, public MixPolicy >::Data { - JSValueType unboxedType_; - - MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType) - : MBinaryInstruction(object, value), unboxedType_(unboxedType) + MArrayPush(MDefinition* object, MDefinition* value) + : MBinaryInstruction(object, value) { setResultType(MIRType::Int32); } @@ -9861,12 +9677,8 @@ class MArrayPush TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, value)) - JSValueType unboxedType() const { - return unboxedType_; - } AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | - AliasSet::BoxedOrUnboxedElements(unboxedType())); + return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); } void computeRange(TempAllocator& alloc) override; @@ -9880,15 +9692,13 @@ class MArraySlice { CompilerObject templateObj_; gc::InitialHeap initialHeap_; - JSValueType unboxedType_; MArraySlice(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* begin, MDefinition* end, - JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType) + JSObject* templateObj, gc::InitialHeap initialHeap) : MTernaryInstruction(obj, begin, end), templateObj_(templateObj), - initialHeap_(initialHeap), - unboxedType_(unboxedType) + initialHeap_(initialHeap) { setResultType(MIRType::Object); } @@ -9906,10 +9716,6 @@ class MArraySlice return initialHeap_; } - JSValueType unboxedType() const { - return unboxedType_; - } - bool possiblyCalls() const override { return true; } @@ -11174,11 +10980,6 @@ class MGuardShape setMovable(); setResultType(MIRType::Object); setResultTypeSet(obj->resultTypeSet()); - - // Disallow guarding on unboxed object shapes. The group is better to - // guard on, and guarding on the shape can interact badly with - // MConvertUnboxedObjectToNative. - MOZ_ASSERT(shape->getObjectClass() != &UnboxedPlainObject::class_); } public: @@ -11273,11 +11074,6 @@ class MGuardObjectGroup setGuard(); setMovable(); setResultType(MIRType::Object); - - // Unboxed groups which might be converted to natives can't be guarded - // on, due to MConvertUnboxedObjectToNative. - MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(), - !group->unboxedLayoutDontCheckGeneration().nativeGroup()); } public: @@ -11386,73 +11182,6 @@ class MGuardClass ALLOW_CLONE(MGuardClass) }; -// Guard on the presence or absence of an unboxed object's expando. -class MGuardUnboxedExpando - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - bool requireExpando_; - BailoutKind bailoutKind_; - - MGuardUnboxedExpando(MDefinition* obj, bool requireExpando, BailoutKind bailoutKind) - : MUnaryInstruction(obj), - requireExpando_(requireExpando), - bailoutKind_(bailoutKind) - { - setGuard(); - setMovable(); - setResultType(MIRType::Object); - } - - public: - INSTRUCTION_HEADER(GuardUnboxedExpando) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool requireExpando() const { - return requireExpando_; - } - BailoutKind bailoutKind() const { - return bailoutKind_; - } - bool congruentTo(const MDefinition* ins) const override { - if (!congruentIfOperandsEqual(ins)) - return false; - if (requireExpando() != ins->toGuardUnboxedExpando()->requireExpando()) - return false; - return true; - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } -}; - -// Load an unboxed plain object's expando. -class MLoadUnboxedExpando - : public MUnaryInstruction, - public SingleObjectPolicy::Data -{ - private: - explicit MLoadUnboxedExpando(MDefinition* object) - : MUnaryInstruction(object) - { - setResultType(MIRType::Object); - setMovable(); - } - - public: - INSTRUCTION_HEADER(LoadUnboxedExpando) - TRIVIAL_NEW_WRAPPERS - NAMED_OPERANDS((0, object)) - - bool congruentTo(const MDefinition* ins) const override { - return congruentIfOperandsEqual(ins); - } - AliasSet getAliasSet() const override { - return AliasSet::Load(AliasSet::ObjectFields); - } -}; - // Load from vp[slot] (slots that are not inline in an object). class MLoadSlot : public MUnaryInstruction, @@ -12355,15 +12084,13 @@ class MInArray { bool needsHoleCheck_; bool needsNegativeIntCheck_; - JSValueType unboxedType_; MInArray(MDefinition* elements, MDefinition* index, MDefinition* initLength, MDefinition* object, - bool needsHoleCheck, JSValueType unboxedType) + bool needsHoleCheck) : MQuaternaryInstruction(elements, index, initLength, object), needsHoleCheck_(needsHoleCheck), - needsNegativeIntCheck_(true), - unboxedType_(unboxedType) + needsNegativeIntCheck_(true) { setResultType(MIRType::Boolean); setMovable(); @@ -12383,9 +12110,6 @@ class MInArray bool needsNegativeIntCheck() const { return needsNegativeIntCheck_; } - JSValueType unboxedType() const { - return unboxedType_; - } void collectRangeInfoPreTrunc() override; AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::Element); @@ -12398,8 +12122,6 @@ class MInArray return false; if (needsNegativeIntCheck() != other->needsNegativeIntCheck()) return false; - if (unboxedType() != other->unboxedType()) - return false; return congruentIfOperandsEqual(other); } }; @@ -14300,8 +14022,6 @@ MDefinition::maybeConstantValue() bool ElementAccessIsDenseNative(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id); -JSValueType UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* obj, - MDefinition* id); bool ElementAccessIsTypedArray(CompilerConstraintList* constraints, MDefinition* obj, MDefinition* id, Scalar::Type* arrayType); diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fddc1e637..ab37604b4 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -187,8 +187,6 @@ namespace jit { _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ - _(GuardUnboxedExpando) \ - _(LoadUnboxedExpando) \ _(ArrayLength) \ _(SetArrayLength) \ _(GetNextEntryForIterator) \ @@ -200,10 +198,6 @@ namespace jit { _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ - _(SetUnboxedArrayInitializedLength) \ _(Not) \ _(BoundsCheck) \ _(BoundsCheckLower) \ @@ -219,7 +213,6 @@ namespace jit { _(StoreUnboxedScalar) \ _(StoreUnboxedObjectOrNull) \ _(StoreUnboxedString) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShift) \ _(ArrayPush) \ _(ArraySlice) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index f633b9b7b..a739b9325 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -126,20 +126,14 @@ MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Regi { // Type set guards might miss when an object's group changes. In this case // either its old group's properties will become unknown, or it will change - // to a native object with an original unboxed group. Jump to label if this - // might have happened for the input object. + // to a native object. Jump to label if this might have happened for the + // input object. if (types->unknownObject()) { jump(label); return; } - loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); - load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch); - and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch); - branch32(Assembler::Equal, - scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label); - for (size_t i = 0; i < types->getObjectCount(); i++) { if (JSObject* singleton = types->getSingletonNoBarrier(i)) { movePtr(ImmGCPtr(singleton), scratch); @@ -468,268 +462,6 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest, bool allowDouble, Register temp, Label* fail); -template -void -MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output) -{ - switch (type) { - case JSVAL_TYPE_INT32: { - // Handle loading an int32 into a double reg. - if (output.type() == MIRType::Double) { - convertInt32ToDouble(address, output.typedReg().fpu()); - break; - } - MOZ_FALLTHROUGH; - } - - case JSVAL_TYPE_BOOLEAN: - case JSVAL_TYPE_STRING: { - Register outReg; - if (output.hasValue()) { - outReg = output.valueReg().scratchReg(); - } else { - MOZ_ASSERT(output.type() == MIRTypeFromValueType(type)); - outReg = output.typedReg().gpr(); - } - - switch (type) { - case JSVAL_TYPE_BOOLEAN: - load8ZeroExtend(address, outReg); - break; - case JSVAL_TYPE_INT32: - load32(address, outReg); - break; - case JSVAL_TYPE_STRING: - loadPtr(address, outReg); - break; - default: - MOZ_CRASH(); - } - - if (output.hasValue()) - tagValue(type, outReg, output.valueReg()); - break; - } - - case JSVAL_TYPE_OBJECT: - if (output.hasValue()) { - Register scratch = output.valueReg().scratchReg(); - loadPtr(address, scratch); - - Label notNull, done; - branchPtr(Assembler::NotEqual, scratch, ImmWord(0), ¬Null); - - moveValue(NullValue(), output.valueReg()); - jump(&done); - - bind(¬Null); - tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg()); - - bind(&done); - } else { - // Reading null can't be possible here, as otherwise the result - // would be a value (either because null has been read before or - // because there is a barrier). - Register reg = output.typedReg().gpr(); - loadPtr(address, reg); -#ifdef DEBUG - Label ok; - branchTestPtr(Assembler::NonZero, reg, reg, &ok); - assumeUnreachable("Null not possible"); - bind(&ok); -#endif - } - break; - - case JSVAL_TYPE_DOUBLE: - // Note: doubles in unboxed objects are not accessed through other - // views and do not need canonicalization. - if (output.hasValue()) - loadValue(address, output.valueReg()); - else - loadDouble(address, output.typedReg().fpu()); - break; - - default: - MOZ_CRASH(); - } -} - -template void -MacroAssembler::loadUnboxedProperty(Address address, JSValueType type, - TypedOrValueRegister output); - -template void -MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type, - TypedOrValueRegister output); - -static void -StoreUnboxedFailure(MacroAssembler& masm, Label* failure) -{ - // Storing a value to an unboxed property is a fallible operation and - // the caller must provide a failure label if a particular unboxed store - // might fail. Sometimes, however, a store that cannot succeed (such as - // storing a string to an int32 property) will be marked as infallible. - // This can only happen if the code involved is unreachable. - if (failure) - masm.jump(failure); - else - masm.assumeUnreachable("Incompatible write to unboxed property"); -} - -template -void -MacroAssembler::storeUnboxedProperty(T address, JSValueType type, - const ConstantOrRegister& value, Label* failure) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - if (value.constant()) { - if (value.value().isBoolean()) - store8(Imm32(value.value().toBoolean()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Boolean) - store8(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1); - } - break; - - case JSVAL_TYPE_INT32: - if (value.constant()) { - if (value.value().isInt32()) - store32(Imm32(value.value().toInt32()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Int32) - store32(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4); - } - break; - - case JSVAL_TYPE_DOUBLE: - if (value.constant()) { - if (value.value().isNumber()) { - loadConstantDouble(value.value().toNumber(), ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - } else { - StoreUnboxedFailure(*this, failure); - } - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::Int32) { - convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - } else if (value.reg().type() == MIRType::Double) { - storeDouble(value.reg().typedReg().fpu(), address); - } else { - StoreUnboxedFailure(*this, failure); - } - } else { - ValueOperand reg = value.reg().valueReg(); - Label notInt32, end; - branchTestInt32(Assembler::NotEqual, reg, ¬Int32); - int32ValueToDouble(reg, ScratchDoubleReg); - storeDouble(ScratchDoubleReg, address); - jump(&end); - bind(¬Int32); - if (failure) - branchTestDouble(Assembler::NotEqual, reg, failure); - storeValue(reg, address); - bind(&end); - } - break; - - case JSVAL_TYPE_OBJECT: - if (value.constant()) { - if (value.value().isObjectOrNull()) - storePtr(ImmGCPtr(value.value().toObjectOrNull()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - MOZ_ASSERT(value.reg().type() != MIRType::Null); - if (value.reg().type() == MIRType::Object) - storePtr(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) { - Label ok; - branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok); - branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure); - bind(&ok); - } - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); - } - break; - - case JSVAL_TYPE_STRING: - if (value.constant()) { - if (value.value().isString()) - storePtr(ImmGCPtr(value.value().toString()), address); - else - StoreUnboxedFailure(*this, failure); - } else if (value.reg().hasTyped()) { - if (value.reg().type() == MIRType::String) - storePtr(value.reg().typedReg().gpr(), address); - else - StoreUnboxedFailure(*this, failure); - } else { - if (failure) - branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure); - storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); - } - break; - - default: - MOZ_CRASH(); - } -} - -template void -MacroAssembler::storeUnboxedProperty(Address address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - -template void -MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - -void -MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index, - Register temp, Label* failure) -{ - Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); - Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); - - Label capacityIsIndex, done; - load32(initLengthAddr, temp); - branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex); - branch32(Assembler::BelowOrEqual, lengthAddr, index, failure); - jump(&done); - bind(&capacityIsIndex); - - // Do a partial shift so that we can get an absolute offset from the base - // of CapacityArray to use. - JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4); - rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp); - and32(Imm32(~0x3), temp); - - addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp); - branch32(Assembler::BelowOrEqual, Address(temp, 0), index, failure); - bind(&done); -} - // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -1277,20 +1009,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t); offset += sizeof(uintptr_t); } - } else if (templateObj->is()) { - storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando())); - if (initContents) - initUnboxedObjectContents(obj, &templateObj->as()); - } else if (templateObj->is()) { - MOZ_ASSERT(templateObj->as().hasInlineElements()); - int elementsOffset = UnboxedArrayObject::offsetOfInlineElements(); - computeEffectiveAddress(Address(obj, elementsOffset), temp); - storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements())); - store32(Imm32(templateObj->as().length()), - Address(obj, UnboxedArrayObject::offsetOfLength())); - uint32_t capacityIndex = templateObj->as().capacityIndex(); - store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift), - Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } else { MOZ_CRASH("Unknown object"); } @@ -1311,29 +1029,6 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, #endif } -void -MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject) -{ - const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration(); - - // Initialize reference fields of the object, per UnboxedPlainObject::create. - if (const int32_t* list = layout.traceList()) { - while (*list != -1) { - storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty), - Address(object, UnboxedPlainObject::offsetOfData() + *list)); - list++; - } - list++; - while (*list != -1) { - storePtr(ImmWord(0), - Address(object, UnboxedPlainObject::offsetOfData() + *list)); - list++; - } - // Unboxed objects don't have Values to initialize. - MOZ_ASSERT(*(list + 1) == -1); - } -} - void MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, Label* fail) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index b6616321c..d5cc95839 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -36,7 +36,6 @@ #include "vm/ProxyObject.h" #include "vm/Shape.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" using mozilla::FloatingPoint; @@ -1626,20 +1625,6 @@ class MacroAssembler : public MacroAssemblerSpecific void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest, unsigned numElems = 0); - // Load a property from an UnboxedPlainObject or UnboxedArrayObject. - template - void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output); - - // Store a property to an UnboxedPlainObject, without triggering barriers. - // If failure is null, the value definitely has a type suitable for storing - // in the property. - template - void storeUnboxedProperty(T address, JSValueType type, - const ConstantOrRegister& value, Label* failure); - - void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index, - Register temp, Label* failure); - Register extractString(const Address& address, Register scratch) { return extractObject(address, scratch); } @@ -1716,8 +1701,6 @@ class MacroAssembler : public MacroAssemblerSpecific LiveRegisterSet liveRegs, Label* fail, TypedArrayObject* templateObj, TypedArrayLength lengthKind); - void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject); - void newGCString(Register result, Register temp, Label* fail); void newGCFatInlineString(Register result, Register temp, Label* fail); diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 308def041..7d72795a0 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -844,8 +844,6 @@ MaybeConstructorFromType(TypeSet::Type ty) return nullptr; ObjectGroup* obj = ty.group(); TypeNewScript* newScript = obj->newScript(); - if (!newScript && obj->maybeUnboxedLayout()) - newScript = obj->unboxedLayout().newScript(); return newScript ? newScript->function() : nullptr; } diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index 13bf9224b..793b631df 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1354,7 +1354,7 @@ RNewArray::recover(JSContext* cx, SnapshotIterator& iter) const RootedValue result(cx); RootedObjectGroup group(cx, templateObject->group()); - JSObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); + ArrayObject* resultObject = NewFullyAllocatedArrayTryUseGroup(cx, group, count_); if (!resultObject) return false; @@ -1539,37 +1539,12 @@ RObjectState::recover(JSContext* cx, SnapshotIterator& iter) const RootedObject object(cx, &iter.read().toObject()); RootedValue val(cx); - if (object->is()) { - const UnboxedLayout& layout = object->as().layout(); + RootedNativeObject nativeObject(cx, &object->as()); + MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); - RootedId id(cx); - RootedValue receiver(cx, ObjectValue(*object)); - const UnboxedLayout::PropertyVector& properties = layout.properties(); - for (size_t i = 0; i < properties.length(); i++) { - val = iter.read(); - - // This is the default placeholder value of MObjectState, when no - // properties are defined yet. - if (val.isUndefined()) - continue; - - id = NameToId(properties[i].name); - ObjectOpResult result; - - // SetProperty can only fail due to OOM. - if (!SetProperty(cx, object, id, val, receiver, result)) - return false; - if (!result) - return result.reportError(cx, object, id); - } - } else { - RootedNativeObject nativeObject(cx, &object->as()); - MOZ_ASSERT(nativeObject->slotSpan() == numSlots()); - - for (size_t i = 0; i < numSlots(); i++) { - val = iter.read(); - nativeObject->setSlot(i, val); - } + for (size_t i = 0; i < numSlots(); i++) { + val = iter.read(); + nativeObject->setSlot(i, val); } val.setObject(*object); diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 4614b2162..be9ceee2e 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -13,7 +13,6 @@ #include "jit/MIR.h" #include "jit/MIRGenerator.h" #include "jit/MIRGraph.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" @@ -183,25 +182,6 @@ IsObjectEscaped(MInstruction* ins, JSObject* objDefault) JitSpewDef(JitSpew_Escape, "is escaped by\n", def); return true; - case MDefinition::Op_LoadUnboxedScalar: - case MDefinition::Op_StoreUnboxedScalar: - case MDefinition::Op_LoadUnboxedObjectOrNull: - case MDefinition::Op_StoreUnboxedObjectOrNull: - case MDefinition::Op_LoadUnboxedString: - case MDefinition::Op_StoreUnboxedString: - // Not escaped if it is the first argument. - if (def->indexOf(*i) != 0) { - JitSpewDef(JitSpew_Escape, "is escaped by\n", def); - return true; - } - - if (!def->getOperand(1)->isConstant()) { - JitSpewDef(JitSpew_Escape, "is addressed with unknown index\n", def); - return true; - } - - break; - case MDefinition::Op_PostWriteBarrier: break; @@ -305,12 +285,6 @@ class ObjectMemoryView : public MDefinitionVisitorDefaultNoop void visitGuardShape(MGuardShape* ins); void visitFunctionEnvironment(MFunctionEnvironment* ins); void visitLambda(MLambda* ins); - void visitStoreUnboxedScalar(MStoreUnboxedScalar* ins); - void visitLoadUnboxedScalar(MLoadUnboxedScalar* ins); - void visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins); - void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins); - void visitStoreUnboxedString(MStoreUnboxedString* ins); - void visitLoadUnboxedString(MLoadUnboxedString* ins); private: void storeOffset(MInstruction* ins, size_t offset, MDefinition* value); @@ -656,21 +630,6 @@ ObjectMemoryView::visitLambda(MLambda* ins) ins->setIncompleteObject(); } -static size_t -GetOffsetOf(MDefinition* index, size_t width, int32_t baseOffset) -{ - int32_t idx = index->toConstant()->toInt32(); - MOZ_ASSERT(idx >= 0); - MOZ_ASSERT(baseOffset >= 0 && size_t(baseOffset) >= UnboxedPlainObject::offsetOfData()); - return idx * width + baseOffset - UnboxedPlainObject::offsetOfData(); -} - -static size_t -GetOffsetOf(MDefinition* index, Scalar::Type type, int32_t baseOffset) -{ - return GetOffsetOf(index, Scalar::byteSize(type), baseOffset); -} - void ObjectMemoryView::storeOffset(MInstruction* ins, size_t offset, MDefinition* value) { @@ -700,77 +659,6 @@ ObjectMemoryView::loadOffset(MInstruction* ins, size_t offset) ins->block()->discard(ins); } -void -ObjectMemoryView::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), ins->storageType(), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - -void -ObjectMemoryView::visitStoreUnboxedObjectOrNull(MStoreUnboxedObjectOrNull* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - // Clone the state and update the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - -void -ObjectMemoryView::visitStoreUnboxedString(MStoreUnboxedString* ins) -{ - // Skip stores made on other objects. - if (ins->elements() != obj_) - return; - - // Clone the state and update the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - storeOffset(ins, offset, ins->value()); -} - -void -ObjectMemoryView::visitLoadUnboxedString(MLoadUnboxedString* ins) -{ - // Skip loads made on other objects. - if (ins->elements() != obj_) - return; - - // Replace load by the slot value. - size_t offset = GetOffsetOf(ins->index(), sizeof(uintptr_t), ins->offsetAdjustment()); - loadOffset(ins, offset); -} - static bool IndexOf(MDefinition* ins, int32_t* res) { @@ -907,11 +795,6 @@ IsArrayEscaped(MInstruction* ins) return true; } - if (obj->is()) { - JitSpew(JitSpew_Escape, "Template object is an unboxed plain object."); - return true; - } - if (length >= 16) { JitSpew(JitSpew_Escape, "Array has too many elements"); return true; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 767cff661..05a95824f 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -27,6 +27,7 @@ #endif #include "jit/VMFunctions.h" #include "vm/Interpreter.h" +#include "vm/NativeObject-inl.h" #include "jit/MacroAssembler-inl.h" #include "vm/Interpreter-inl.h" @@ -285,11 +286,6 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape"); break; } - case ICStub::GetElem_UnboxedArray: { - ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray(); - TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group"); - break; - } case ICStub::GetElem_TypedArray: { ICGetElem_TypedArray* getElemStub = toGetElem_TypedArray(); TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-typedarray-shape"); @@ -2248,9 +2244,7 @@ IsCacheableProtoChain(JSObject* obj, JSObject* holder, bool isDOMProxy) if (!isDOMProxy && !obj->isNative()) { if (obj == holder) return false; - if (!obj->is() && - !obj->is() && - !obj->is()) + if (!obj->is()) { return false; } @@ -2578,12 +2572,6 @@ CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name, } else if (curObj != obj) { // Non-native objects are only handled as the original receiver. return false; - } else if (curObj->is()) { - if (curObj->as().containsUnboxedOrExpandoProperty(cx, NameToId(name))) - return false; - } else if (curObj->is()) { - if (name == cx->names().length) - return false; } else if (curObj->is()) { if (curObj->as().typeDescr().hasProperty(cx->names(), NameToId(name))) return false; @@ -2848,34 +2836,15 @@ GuardReceiverObject(MacroAssembler& masm, ReceiverGuard guard, { Address groupAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfGroup()); Address shapeAddress(ICStubReg, receiverGuardOffset + HeapReceiverGuard::offsetOfShape()); - Address expandoAddress(object, UnboxedPlainObject::offsetOfExpando()); if (guard.group) { masm.loadPtr(groupAddress, scratch); masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure); - - if (guard.group->clasp() == &UnboxedPlainObject::class_ && !guard.shape) { - // Guard the unboxed object has no expando object. - masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure); - } } if (guard.shape) { masm.loadPtr(shapeAddress, scratch); - if (guard.group && guard.group->clasp() == &UnboxedPlainObject::class_) { - // Guard the unboxed object has a matching expando object. - masm.branchPtr(Assembler::Equal, expandoAddress, ImmWord(0), failure); - Label done; - masm.push(object); - masm.loadPtr(expandoAddress, object); - masm.branchTestObjShape(Assembler::Equal, object, scratch, &done); - masm.pop(object); - masm.jump(failure); - masm.bind(&done); - masm.pop(object); - } else { - masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); - } + masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure); } } @@ -4259,8 +4228,7 @@ DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHan return false; if (!stub->invalid() && - (templateObject->is() || - !templateObject->as().hasDynamicSlots())) + !templateObject->as().hasDynamicSlots()) { JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject); if (!code) diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 77b9e3647..402d910b9 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -28,7 +28,7 @@ #include "vm/NativeObject-inl.h" #include "vm/StringObject-inl.h" #include "vm/TypeInference-inl.h" -#include "vm/UnboxedObject-inl.h" +#include "gc/StoreBuffer-inl.h" using namespace js; using namespace js::jit; @@ -318,7 +318,7 @@ ArraySpliceDense(JSContext* cx, HandleObject obj, uint32_t start, uint32_t delet bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) { - MOZ_ASSERT(obj->is() || obj->is()); + MOZ_ASSERT(obj->is()); AutoDetectInvalidation adi(cx, rval); @@ -337,12 +337,11 @@ ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) } bool -ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) +ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v, uint32_t* length) { - *length = GetAnyBoxedOrUnboxedArrayLength(obj); - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, - ShouldUpdateTypes::DontUpdate); + *length = arr->length(); + DenseElementResult result = arr->setOrExtendDenseElements(cx, *length, v.address(), 1, + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) { (*length)++; return result == DenseElementResult::Success; @@ -350,7 +349,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) JS::AutoValueArray<3> argv(cx); argv[0].setUndefined(); - argv[1].setObject(*obj); + argv[1].setObject(*arr); argv[2].set(v); if (!js::array_push(cx, 1, argv.begin())) return false; @@ -362,7 +361,7 @@ ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length) bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) { - MOZ_ASSERT(obj->is() || obj->is()); + MOZ_ASSERT(obj->is()); AutoDetectInvalidation adi(cx, rval); @@ -1143,16 +1142,14 @@ Recompile(JSContext* cx) } bool -SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict) +SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, HandleValue value, bool strict) { // This function is called from Ion code for StoreElementHole's OOL path. - // In this case we know the object is native or an unboxed array and that - // no type changes are needed. + // In this case we know the object is native and that no type changes are + // needed. - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, index, value.address(), 1, - ShouldUpdateTypes::DontUpdate); + DenseElementResult result = obj->setOrExtendDenseElements(cx, index, value.address(), 1, + ShouldUpdateTypes::DontUpdate); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h index 572f05373..9a2edd0f3 100644 --- a/js/src/jit/VMFunctions.h +++ b/js/src/jit/VMFunctions.h @@ -622,7 +622,7 @@ template bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res); MOZ_MUST_USE bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); -MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length); +MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length); MOZ_MUST_USE bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval); JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep); @@ -748,8 +748,8 @@ ForcedRecompile(JSContext* cx); JSString* StringReplace(JSContext* cx, HandleString string, HandleString pattern, HandleString repl); -MOZ_MUST_USE bool SetDenseOrUnboxedArrayElement(JSContext* cx, HandleObject obj, int32_t index, - HandleValue value, bool strict); +MOZ_MUST_USE bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index, + HandleValue value, bool strict); void AssertValidObjectPtr(JSContext* cx, JSObject* obj); void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj); diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index f4adcc63c..ffa4d8367 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5165,72 +5165,6 @@ class LSetInitializedLength : public LInstructionHelper<0, 2, 0> } }; -class LUnboxedArrayLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayLength) - - explicit LUnboxedArrayLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LUnboxedArrayInitializedLength : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(UnboxedArrayInitializedLength) - - explicit LUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LIncrementUnboxedArrayInitializedLength : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(IncrementUnboxedArrayInitializedLength) - - explicit LIncrementUnboxedArrayInitializedLength(const LAllocation& object) { - setOperand(0, object); - } - - const LAllocation* object() { - return getOperand(0); - } -}; - -class LSetUnboxedArrayInitializedLength : public LInstructionHelper<0, 2, 1> -{ - public: - LIR_HEADER(SetUnboxedArrayInitializedLength) - - explicit LSetUnboxedArrayInitializedLength(const LAllocation& object, - const LAllocation& length, - const LDefinition& temp) { - setOperand(0, object); - setOperand(1, length); - setTemp(0, temp); - } - - const LAllocation* object() { - return getOperand(0); - } - const LAllocation* length() { - return getOperand(1); - } - const LDefinition* temp() { - return getTemp(0); - } -}; - // Load the length from an elements header. class LArrayLength : public LInstructionHelper<1, 1, 0> { @@ -5735,19 +5669,17 @@ class LStoreElementT : public LInstructionHelper<0, 3, 0> }; // Like LStoreElementV, but supports indexes >= initialized length. -class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> +class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> { public: LIR_HEADER(StoreElementHoleV) LStoreElementHoleV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LBoxAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); - setTemp(0, temp); } static const size_t Value = 3; @@ -5767,19 +5699,17 @@ class LStoreElementHoleV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> }; // Like LStoreElementT, but supports indexes >= initialized length. -class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> +class LStoreElementHoleT : public LInstructionHelper<0, 4, 0> { public: LIR_HEADER(StoreElementHoleT) LStoreElementHoleT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); - setTemp(0, temp); } const MStoreElementHole* mir() const { @@ -5800,19 +5730,17 @@ class LStoreElementHoleT : public LInstructionHelper<0, 4, 1> }; // Like LStoreElementV, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> +class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 0> { public: LIR_HEADER(FallibleStoreElementV) LFallibleStoreElementV(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LBoxAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LBoxAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setBoxOperand(Value, value); - setTemp(0, temp); } static const size_t Value = 3; @@ -5832,19 +5760,17 @@ class LFallibleStoreElementV : public LInstructionHelper<0, 3 + BOX_PIECES, 1> }; // Like LStoreElementT, but can just ignore assignment (for eg. frozen objects) -class LFallibleStoreElementT : public LInstructionHelper<0, 4, 1> +class LFallibleStoreElementT : public LInstructionHelper<0, 4, 0> { public: LIR_HEADER(FallibleStoreElementT) LFallibleStoreElementT(const LAllocation& object, const LAllocation& elements, - const LAllocation& index, const LAllocation& value, - const LDefinition& temp) { + const LAllocation& index, const LAllocation& value) { setOperand(0, object); setOperand(1, elements); setOperand(2, index); setOperand(3, value); - setTemp(0, temp); } const MFallibleStoreElement* mir() const { @@ -5891,22 +5817,6 @@ class LStoreUnboxedPointer : public LInstructionHelper<0, 3, 0> } }; -// If necessary, convert an unboxed object in a particular group to its native -// representation. -class LConvertUnboxedObjectToNative : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(ConvertUnboxedObjectToNative) - - explicit LConvertUnboxedObjectToNative(const LAllocation& object) { - setOperand(0, object); - } - - MConvertUnboxedObjectToNative* mir() { - return mir_->toConvertUnboxedObjectToNative(); - } -}; - class LArrayPopShiftV : public LInstructionHelper { public: @@ -7429,38 +7339,6 @@ class LGuardReceiverPolymorphic : public LInstructionHelper<0, 1, 1> } }; -class LGuardUnboxedExpando : public LInstructionHelper<0, 1, 0> -{ - public: - LIR_HEADER(GuardUnboxedExpando) - - explicit LGuardUnboxedExpando(const LAllocation& in) { - setOperand(0, in); - } - const LAllocation* object() { - return getOperand(0); - } - const MGuardUnboxedExpando* mir() const { - return mir_->toGuardUnboxedExpando(); - } -}; - -class LLoadUnboxedExpando : public LInstructionHelper<1, 1, 0> -{ - public: - LIR_HEADER(LoadUnboxedExpando) - - explicit LLoadUnboxedExpando(const LAllocation& in) { - setOperand(0, in); - } - const LAllocation* object() { - return getOperand(0); - } - const MLoadUnboxedExpando* mir() const { - return mir_->toLoadUnboxedExpando(); - } -}; - // Guard that a value is in a TypeSet. class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index fe2ab5ea3..deae92839 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -258,8 +258,6 @@ _(GuardObjectGroup) \ _(GuardObjectIdentity) \ _(GuardClass) \ - _(GuardUnboxedExpando) \ - _(LoadUnboxedExpando) \ _(TypeBarrierV) \ _(TypeBarrierO) \ _(MonitorTypes) \ @@ -269,10 +267,6 @@ _(PostWriteElementBarrierV) \ _(InitializedLength) \ _(SetInitializedLength) \ - _(UnboxedArrayLength) \ - _(UnboxedArrayInitializedLength) \ - _(IncrementUnboxedArrayInitializedLength) \ - _(SetUnboxedArrayInitializedLength) \ _(BoundsCheck) \ _(BoundsCheckRange) \ _(BoundsCheckLower) \ @@ -287,7 +281,6 @@ _(StoreElementT) \ _(StoreUnboxedScalar) \ _(StoreUnboxedPointer) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShiftV) \ _(ArrayPopShiftT) \ _(ArrayPushV) \ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f78f94dc1..9ee29ffe4 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6434,9 +6434,6 @@ JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t v } jit::JitOptions.jumpThreshold = value; break; - case JSJITCOMPILER_UNBOXED_OBJECTS: - jit::JitOptions.disableUnboxedObjects = !value; - break; case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE: jit::JitOptions.asmJSAtomicsEnable = !!value; break; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 1f726f2e5..3f65dad34 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1093,7 +1093,6 @@ class JS_PUBLIC_API(ContextOptions) { wasmAlwaysBaseline_(false), throwOnAsmJSValidationFailure_(false), nativeRegExp_(true), - unboxedArrays_(false), asyncStack_(true), throwOnDebuggeeWouldRun_(true), dumpStackOnDebuggeeWouldRun_(false), @@ -1170,12 +1169,6 @@ class JS_PUBLIC_API(ContextOptions) { return *this; } - bool unboxedArrays() const { return unboxedArrays_; } - ContextOptions& setUnboxedArrays(bool flag) { - unboxedArrays_ = flag; - return *this; - } - bool asyncStack() const { return asyncStack_; } ContextOptions& setAsyncStack(bool flag) { asyncStack_ = flag; @@ -1238,7 +1231,6 @@ class JS_PUBLIC_API(ContextOptions) { bool wasmAlwaysBaseline_ : 1; bool throwOnAsmJSValidationFailure_ : 1; bool nativeRegExp_ : 1; - bool unboxedArrays_ : 1; bool asyncStack_ : 1; bool throwOnDebuggeeWouldRun_ : 1; bool dumpStackOnDebuggeeWouldRun_ : 1; @@ -5783,20 +5775,19 @@ JS_SetParallelParsingEnabled(JSContext* cx, bool enabled); extern JS_PUBLIC_API(void) JS_SetOffthreadIonCompilationEnabled(JSContext* cx, bool enabled); -#define JIT_COMPILER_OPTIONS(Register) \ - Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \ - Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \ - Register(ION_GVN_ENABLE, "ion.gvn.enable") \ - Register(ION_FORCE_IC, "ion.forceinlineCaches") \ - Register(ION_ENABLE, "ion.enable") \ +#define JIT_COMPILER_OPTIONS(Register) \ + Register(BASELINE_WARMUP_TRIGGER, "baseline.warmup.trigger") \ + Register(ION_WARMUP_TRIGGER, "ion.warmup.trigger") \ + Register(ION_GVN_ENABLE, "ion.gvn.enable") \ + Register(ION_FORCE_IC, "ion.forceinlineCaches") \ + Register(ION_ENABLE, "ion.enable") \ Register(ION_INTERRUPT_WITHOUT_SIGNAL, "ion.interrupt-without-signals") \ - Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \ - Register(BASELINE_ENABLE, "baseline.enable") \ - Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ - Register(JUMP_THRESHOLD, "jump-threshold") \ - Register(UNBOXED_OBJECTS, "unboxed_objects") \ - Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ - Register(WASM_TEST_MODE, "wasm.test-mode") \ + Register(ION_CHECK_RANGE_ANALYSIS, "ion.check-range-analysis") \ + Register(BASELINE_ENABLE, "baseline.enable") \ + Register(OFFTHREAD_COMPILATION_ENABLE, "offthread-compilation.enable") \ + Register(JUMP_THRESHOLD, "jump-threshold") \ + Register(ASMJS_ATOMICS_ENABLE, "asmjs.atomics.enable") \ + Register(WASM_TEST_MODE, "wasm.test-mode") \ Register(WASM_FOLD_OFFSETS, "wasm.fold-offsets") typedef enum JSJitCompilerOption { diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3b4c957dc..ec65bce64 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -45,7 +45,6 @@ #include "vm/Caches-inl.h" #include "vm/Interpreter-inl.h" #include "vm/NativeObject-inl.h" -#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::gc; @@ -64,7 +63,7 @@ using JS::ToUint32; bool JS::IsArray(JSContext* cx, HandleObject obj, IsArrayAnswer* answer) { - if (obj->is() || obj->is()) { + if (obj->is()) { *answer = IsArrayAnswer::Array; return true; } @@ -100,11 +99,6 @@ js::GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp) return true; } - if (obj->is()) { - *lengthp = obj->as().length(); - return true; - } - if (obj->is()) { ArgumentsObject& argsobj = obj->as(); if (!argsobj.hasOverriddenLength()) { @@ -253,18 +247,20 @@ static bool GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, bool* hole, MutableHandleValue vp) { - AssertGreaterThanZero(index); - if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) { - vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index))); - if (!vp.isMagic(JS_ELEMENTS_HOLE)) { - *hole = false; - return true; + if (obj->isNative()) { + NativeObject* nobj = &obj->as(); + if (index < nobj->getDenseInitializedLength()) { + vp.set(nobj->getDenseElement(size_t(index))); + if (!vp.isMagic(JS_ELEMENTS_HOLE)) { + *hole = false; + return true; + } } - } - if (obj->is()) { - if (obj->as().maybeGetElement(uint32_t(index), vp)) { - *hole = false; - return true; + if (nobj->is() && index <= UINT32_MAX) { + if (nobj->as().maybeGetElement(uint32_t(index), vp)) { + *hole = false; + return true; + } } } @@ -283,8 +279,8 @@ ElementAdder::append(JSContext* cx, HandleValue v) { MOZ_ASSERT(index_ < length_); if (resObj_) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1); + NativeObject* resObj = &resObj_->as(); + DenseElementResult result = resObj->setOrExtendDenseElements(cx, index_, v.address(), 1); if (result == DenseElementResult::Failure) return false; if (result == DenseElementResult::Incomplete) { @@ -336,37 +332,31 @@ js::GetElementsWithAdder(JSContext* cx, HandleObject obj, HandleObject receiver, return true; } -template -DenseElementResult -GetBoxedOrUnboxedDenseElements(JSObject* aobj, uint32_t length, Value* vp) +static bool +GetDenseElements(NativeObject* aobj, uint32_t length, Value* vp) { MOZ_ASSERT(!ObjectMayHaveExtraIndexedProperties(aobj)); - if (length > GetBoxedOrUnboxedInitializedLength(aobj)) - return DenseElementResult::Incomplete; + if (length > aobj->getDenseInitializedLength()) + return false; for (size_t i = 0; i < length; i++) { - vp[i] = GetBoxedOrUnboxedDenseElement(aobj, i); + vp[i] = aobj->getDenseElement(i); // No other indexed properties so hole => undefined. if (vp[i].isMagic(JS_ELEMENTS_HOLE)) vp[i] = UndefinedValue(); } - return DenseElementResult::Success; + return true; } -DefineBoxedOrUnboxedFunctor3(GetBoxedOrUnboxedDenseElements, - JSObject*, uint32_t, Value*); - bool js::GetElements(JSContext* cx, HandleObject aobj, uint32_t length, Value* vp) { if (!ObjectMayHaveExtraIndexedProperties(aobj)) { - GetBoxedOrUnboxedDenseElementsFunctor functor(aobj, length, vp); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, aobj); - if (result != DenseElementResult::Incomplete) - return result == DenseElementResult::Success; + if (GetDenseElements(&aobj->as(), length, vp)) + return true; } if (aobj->is()) { @@ -398,9 +388,9 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v) { MOZ_ASSERT(index >= 0); - if ((obj->is() || obj->is()) && !obj->isIndexed() && index <= UINT32_MAX) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, uint32_t(index), v.address(), 1); + if (obj->is() && !obj->isIndexed() && index <= UINT32_MAX) { + NativeObject* nobj = &obj->as(); + DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(index), v.address(), 1); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; } @@ -520,24 +510,6 @@ struct ReverseIndexComparator } }; -bool -js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen) -{ - double d; - - if (!ToUint32(cx, v, newLen)) - return false; - - if (!ToNumber(cx, v, &d)) - return false; - - if (d == *newLen) - return true; - - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); - return false; -} - /* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */ bool js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, @@ -559,12 +531,22 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, } else { // Step 2 is irrelevant in our implementation. - // Steps 3-7. - MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined()); - if (!CanonicalizeArrayLengthValue(cx, value, &newLen)) + // Step 3. + if (!ToUint32(cx, value, &newLen)) return false; - // Step 8 is irrelevant in our implementation. + // Step 4. + double d; + if (!ToNumber(cx, value, &d)) + return false; + + // Step 5. + if (d != newLen) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); + return false; + } + + // Steps 6-8 are irrelevant in our implementation. } // Steps 9-11. @@ -823,7 +805,7 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) static inline bool ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj) { - return (!obj->isNative() && !obj->is()) || + return !obj->isNative() || obj->isIndexed() || obj->is() || ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames, @@ -854,7 +836,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject* obj) if (ObjectMayHaveExtraIndexedOwnProperties(obj)) return true; - if (GetAnyBoxedOrUnboxedInitializedLength(obj) != 0) + if (obj->as().getDenseInitializedLength() != 0) return true; } while (true); } @@ -1064,31 +1046,32 @@ struct StringSeparatorOp } }; -template -static DenseElementResult -ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length, +template +static bool +ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleNativeObject obj, uint64_t length, StringBuffer& sb, uint32_t* numProcessed) { // This loop handles all elements up to initializedLength. If // length > initLength we rely on the second loop to add the // other elements. MOZ_ASSERT(*numProcessed == 0); - uint32_t initLength = Min(GetBoxedOrUnboxedInitializedLength(obj), length); + uint32_t initLength = Min(obj->getDenseInitializedLength(), + length); while (*numProcessed < initLength) { if (!CheckForInterrupt(cx)) - return DenseElementResult::Failure; + return false; - Value elem = GetBoxedOrUnboxedDenseElement(obj, *numProcessed); + Value elem = obj->as().getDenseElement(*numProcessed); if (elem.isString()) { if (!sb.append(elem.toString())) - return DenseElementResult::Failure; + return false; } else if (elem.isNumber()) { if (!NumberValueToStringBuffer(cx, elem, sb)) - return DenseElementResult::Failure; + return false; } else if (elem.isBoolean()) { if (!BooleanToStringBuffer(elem.toBoolean(), sb)) - return DenseElementResult::Failure; + return false; } else if (elem.isObject() || elem.isSymbol()) { /* * Object stringifying could modify the initialized length or make @@ -1104,32 +1087,12 @@ ArrayJoinDenseKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_ } if (++(*numProcessed) != length && !sepOp(cx, sb)) - return DenseElementResult::Failure; + return false; } - return DenseElementResult::Incomplete; + return true; } -template -struct ArrayJoinDenseKernelFunctor { - JSContext* cx; - SeparatorOp sepOp; - HandleObject obj; - uint32_t length; - StringBuffer& sb; - uint32_t* numProcessed; - - ArrayJoinDenseKernelFunctor(JSContext* cx, SeparatorOp sepOp, HandleObject obj, - uint32_t length, StringBuffer& sb, uint32_t* numProcessed) - : cx(cx), sepOp(sepOp), obj(obj), length(length), sb(sb), numProcessed(numProcessed) - {} - - template - DenseElementResult operator()() { - return ArrayJoinDenseKernel(cx, sepOp, obj, length, sb, numProcessed); - } -}; - template static bool ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length, @@ -1138,10 +1101,10 @@ ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t len uint32_t i = 0; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - ArrayJoinDenseKernelFunctor functor(cx, sepOp, obj, length, sb, &i); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); - if (result == DenseElementResult::Failure) + if (!ArrayJoinDenseKernel(cx, sepOp, obj.as(), length, sb, &i)) + { return false; + } } if (i != length) { @@ -1212,11 +1175,14 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) // An optimized version of a special case of steps 7-11: when length==1 and // the 0th element is a string, ToString() of that element is a no-op and // so it can be immediately returned as the result. - if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) { - Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0); - if (elem0.isString()) { - args.rval().set(elem0); - return true; + if (length == 1 && obj->isNative()) { + NativeObject* nobj = &obj->as(); + if (nobj->getDenseInitializedLength() == 1) { + Value elem0 = nobj->getDenseElement(0); + if (elem0.isString()) { + args.rval().set(elem0); + return true; + } } } @@ -1259,7 +1225,7 @@ js::array_join(JSContext* cx, unsigned argc, Value* vp) } // Step 11 - JSString *str = sb.finishString(); + JSString* str = sb.finishString(); if (!str) return false; @@ -1288,10 +1254,6 @@ array_toLocaleString(JSContext* cx, unsigned argc, Value* vp) args.rval().setString(cx->names().empty); return true; } - if (obj->is() && obj->as().length() == 0) { - args.rval().setString(cx->names().empty); - return true; - } AutoCycleDetector detector(cx, obj); if (!detector.init()) @@ -1328,8 +1290,9 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, return false; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, start, vector, count, updateTypes); + NativeObject* nobj = &obj->as(); + DenseElementResult result = nobj->setOrExtendDenseElements(cx, uint32_t(start), vector, + count, updateTypes); if (result != DenseElementResult::Incomplete) return result == DenseElementResult::Success; } @@ -1363,54 +1326,45 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, return true; } -template -DenseElementResult -ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length) +static DenseElementResult +ArrayReverseDenseKernel(JSContext* cx, HandleNativeObject obj, uint32_t length) { /* An empty array or an array with no elements is already reversed. */ - if (length == 0 || GetBoxedOrUnboxedInitializedLength(obj) == 0) + if (length == 0 || obj->getDenseInitializedLength() == 0) return DenseElementResult::Success; - if (Type == JSVAL_TYPE_MAGIC) { - if (obj->as().denseElementsAreFrozen()) - return DenseElementResult::Incomplete; + if (obj->denseElementsAreFrozen()) + return DenseElementResult::Incomplete; - /* - * It's actually surprisingly complicated to reverse an array due to the - * orthogonality of array length and array capacity while handling - * leading and trailing holes correctly. Reversing seems less likely to - * be a common operation than other array mass-mutation methods, so for - * now just take a probably-small memory hit (in the absence of too many - * holes in the array at its start) and ensure that the capacity is - * sufficient to hold all the elements in the array if it were full. - */ - DenseElementResult result = obj->as().ensureDenseElements(cx, length, 0); - if (result != DenseElementResult::Success) - return result; + /* + * It's actually surprisingly complicated to reverse an array due to the + * orthogonality of array length and array capacity while handling + * leading and trailing holes correctly. Reversing seems less likely to + * be a common operation than other array mass-mutation methods, so for + * now just take a probably-small memory hit (in the absence of too many + * holes in the array at its start) and ensure that the capacity is + * sufficient to hold all the elements in the array if it were full. + */ + DenseElementResult result = obj->ensureDenseElements(cx, length, 0); + if (result != DenseElementResult::Success) + return result; - /* Fill out the array's initialized length to its proper length. */ - obj->as().ensureDenseInitializedLength(cx, length, 0); - } else { - // Unboxed arrays can only be reversed here if their initialized length - // matches their actual length. Otherwise the reversal will place holes - // at the beginning of the array, which we don't support. - if (length != obj->as().initializedLength()) - return DenseElementResult::Incomplete; - } + /* Fill out the array's initialized length to its proper length. */ + obj->ensureDenseInitializedLength(cx, length, 0); RootedValue origlo(cx), orighi(cx); uint32_t lo = 0, hi = length - 1; for (; lo < hi; lo++, hi--) { - origlo = GetBoxedOrUnboxedDenseElement(obj, lo); - orighi = GetBoxedOrUnboxedDenseElement(obj, hi); - SetBoxedOrUnboxedDenseElementNoTypeChange(obj, lo, orighi); + origlo = obj->getDenseElement(lo); + orighi = obj->getDenseElement(hi); + obj->setDenseElement(lo, orighi); if (orighi.isMagic(JS_ELEMENTS_HOLE) && !SuppressDeletedProperty(cx, obj, INT_TO_JSID(lo))) { return DenseElementResult::Failure; } - SetBoxedOrUnboxedDenseElementNoTypeChange(obj, hi, origlo); + obj->setDenseElement(hi, origlo); if (origlo.isMagic(JS_ELEMENTS_HOLE) && !SuppressDeletedProperty(cx, obj, INT_TO_JSID(hi))) { @@ -1421,9 +1375,6 @@ ArrayReverseDenseKernel(JSContext* cx, HandleObject obj, uint32_t length) return DenseElementResult::Success; } -DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel, - JSContext*, HandleObject, uint32_t); - bool js::array_reverse(JSContext* cx, unsigned argc, Value* vp) { @@ -1438,8 +1389,8 @@ js::array_reverse(JSContext* cx, unsigned argc, Value* vp) return false; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - ArrayReverseDenseKernelFunctor functor(cx, obj, len); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); + DenseElementResult result = + ArrayReverseDenseKernel(cx, obj.as(), uint32_t(len)); if (result != DenseElementResult::Incomplete) { /* * Per ECMA-262, don't update the length of the array, even if the new @@ -2082,8 +2033,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) if (!ObjectMayHaveExtraIndexedProperties(obj)) { DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length, - args.array(), args.length()); + obj->as().setOrExtendDenseElements(cx, uint32_t(length), + args.array(), args.length()); if (result != DenseElementResult::Incomplete) { if (result == DenseElementResult::Failure) return false; @@ -2091,14 +2042,8 @@ js::array_push(JSContext* cx, unsigned argc, Value* vp) uint32_t newlength = length + args.length(); args.rval().setNumber(newlength); - // SetOrExtendAnyBoxedOrUnboxedDenseElements takes care of updating the - // length for boxed and unboxed arrays. Handle updates to the length of - // non-arrays here. - bool isArray; - if (!IsArray(cx, obj, &isArray)) - return false; - - if (!isArray) + // Handle updates to the length of non-arrays here. + if (!obj->is()) return SetLengthProperty(cx, obj, newlength); return true; @@ -2154,42 +2099,46 @@ js::array_pop(JSContext* cx, unsigned argc, Value* vp) return SetLengthProperty(cx, obj, index); } -template -static inline DenseElementResult -ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj) +void +js::ArrayShiftMoveElements(NativeObject* obj) { - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(obj)); + MOZ_ASSERT_IF(obj->is(), obj->as().lengthIsWritable()); + + size_t initlen = obj->getDenseInitializedLength(); + MOZ_ASSERT(initlen > 0); /* * At this point the length and initialized length have already been * decremented and the result fetched, so just shift the array elements * themselves. */ - size_t initlen = GetBoxedOrUnboxedInitializedLength(obj); - if (Type == JSVAL_TYPE_MAGIC) { - obj->as().moveDenseElementsNoPreBarrier(0, 1, initlen); - } else { - uint8_t* data = obj->as().elements(); - size_t elementSize = UnboxedTypeSize(Type); - memmove(data, data + elementSize, initlen * elementSize); - } + obj->moveDenseElementsNoPreBarrier(0, 1, initlen); +} + +static inline void +SetInitializedLength(JSContext* cx, NativeObject* obj, size_t initlen) +{ + size_t oldInitlen = obj->getDenseInitializedLength(); + obj->setDenseInitializedLength(initlen); + if (initlen < oldInitlen) + obj->shrinkElements(cx, initlen); +} + +static DenseElementResult +MoveDenseElements(JSContext* cx, NativeObject* obj, uint32_t dstStart, uint32_t srcStart, + uint32_t length) +{ + if (obj->denseElementsAreFrozen()) + return DenseElementResult::Incomplete; + + if (!obj->maybeCopyElementsForWrite(cx)) + return DenseElementResult::Failure; + obj->moveDenseElements(dstStart, srcStart, length); return DenseElementResult::Success; } -DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*); - -void -js::ArrayShiftMoveElements(JSObject* obj) -{ - MOZ_ASSERT_IF(obj->is(), obj->as().lengthIsWritable()); - - ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj); - JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success); -} - -template -DenseElementResult +static DenseElementResult ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval) { if (ObjectMayHaveExtraIndexedProperties(obj)) @@ -2202,25 +2151,22 @@ ArrayShiftDenseKernel(JSContext* cx, HandleObject obj, MutableHandleValue rval) if (MOZ_UNLIKELY(group->hasAllFlags(OBJECT_FLAG_ITERATED))) return DenseElementResult::Incomplete; - size_t initlen = GetBoxedOrUnboxedInitializedLength(obj); + size_t initlen = obj->as().getDenseInitializedLength(); if (initlen == 0) return DenseElementResult::Incomplete; - rval.set(GetBoxedOrUnboxedDenseElement(obj, 0)); + rval.set(obj->as().getDenseElement(0)); if (rval.isMagic(JS_ELEMENTS_HOLE)) rval.setUndefined(); - DenseElementResult result = MoveBoxedOrUnboxedDenseElements(cx, obj, 0, 1, initlen - 1); + DenseElementResult result = MoveDenseElements(cx, &obj->as(), 0, 1, initlen - 1); if (result != DenseElementResult::Success) return result; - SetBoxedOrUnboxedInitializedLength(cx, obj, initlen - 1); + SetInitializedLength(cx, obj.as(), initlen - 1); return DenseElementResult::Success; } -DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel, - JSContext*, HandleObject, MutableHandleValue); - /* ES5 15.4.4.9 */ bool js::array_shift(JSContext* cx, unsigned argc, Value* vp) @@ -2252,8 +2198,7 @@ js::array_shift(JSContext* cx, unsigned argc, Value* vp) uint32_t newlen = len - 1; /* Fast paths. */ - ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval()); - DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj); + DenseElementResult result = ArrayShiftDenseKernel(cx, obj, args.rval()); if (result != DenseElementResult::Incomplete) { if (result == DenseElementResult::Failure) return false; @@ -2307,9 +2252,6 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) if (args.length() > 0) { /* Slide up the array to make room for all args at the bottom. */ if (length > 0) { - // Only include a fast path for boxed arrays. Unboxed arrays can'nt - // be optimized here because unshifting temporarily places holes at - // the start of the array. bool optimized = false; do { if (!obj->is()) @@ -2369,10 +2311,10 @@ js::array_unshift(JSContext* cx, unsigned argc, Value* vp) } /* - * Returns true if this is a dense or unboxed array whose |count| properties - * starting from |startingIndex| may be accessed (get, set, delete) directly - * through its contiguous vector of elements without fear of getters, setters, - * etc. along the prototype chain, or of enumerators requiring notification of + * Returns true if this is a dense array whose properties ending at |endIndex| + * (exclusive) may be accessed (get, set, delete) directly through its + * contiguous vector of elements without fear of getters, setters, etc. along + * the prototype chain, or of enumerators requiring notification of * modifications. */ static inline bool @@ -2383,11 +2325,11 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co return false; /* There's no optimizing possible if it's not an array. */ - if (!arr->is() && !arr->is()) + if (!arr->is()) return false; /* If it's a frozen array, always pick the slow path */ - if (arr->is() && arr->as().denseElementsAreFrozen()) + if (arr->as().denseElementsAreFrozen()) return false; /* @@ -2419,7 +2361,23 @@ CanOptimizeForDenseStorage(HandleObject arr, uint32_t startingIndex, uint32_t co * is subsumed by the initializedLength comparison.) */ return !ObjectMayHaveExtraIndexedProperties(arr) && - startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr); + startingIndex + count <= arr->as().getDenseInitializedLength(); +} + +static inline DenseElementResult +CopyDenseElements(JSContext* cx, NativeObject* dst, NativeObject* src, + uint32_t dstStart, uint32_t srcStart, uint32_t length) +{ + MOZ_ASSERT(dst->getDenseInitializedLength() == dstStart); + MOZ_ASSERT(src->getDenseInitializedLength() >= srcStart + length); + MOZ_ASSERT(dst->getDenseCapacity() >= dstStart + length); + + dst->setDenseInitializedLength(dstStart + length); + + const Value* vp = src->getDenseElements() + srcStart; + dst->initDenseElements(dstStart, vp, length); + + return DenseElementResult::Success; } /* ES 2016 draft Mar 25, 2016 22.1.3.26. */ @@ -2520,7 +2478,9 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI /* Steps 10-11. */ DebugOnly result = - CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount); + CopyDenseElements(cx, &arr->as(), + &obj->as(), 0, + actualStart, actualDeleteCount); MOZ_ASSERT(result.value == DenseElementResult::Success); /* Step 12 (implicit). */ @@ -2557,14 +2517,13 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI if (CanOptimizeForDenseStorage(obj, 0, len, cx)) { /* Steps 15.a-b. */ DenseElementResult result = - MoveAnyBoxedOrUnboxedDenseElements(cx, obj, targetIndex, sourceIndex, - len - sourceIndex); + MoveDenseElements(cx, &obj->as(), targetIndex, sourceIndex, len - sourceIndex); MOZ_ASSERT(result != DenseElementResult::Incomplete); if (result == DenseElementResult::Failure) return false; /* Steps 15.c-d. */ - SetAnyBoxedOrUnboxedInitializedLength(cx, obj, finalLength); + SetInitializedLength(cx, obj.as(), finalLength); } else { /* * This is all very slow if the length is very large. We don't yet @@ -2644,15 +2603,15 @@ js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueI if (CanOptimizeForDenseStorage(obj, len, itemCount - actualDeleteCount, cx)) { DenseElementResult result = - MoveAnyBoxedOrUnboxedDenseElements(cx, obj, actualStart + itemCount, - actualStart + actualDeleteCount, - len - (actualStart + actualDeleteCount)); + MoveDenseElements(cx, &obj->as(), actualStart + itemCount, + actualStart + actualDeleteCount, + len - (actualStart + actualDeleteCount)); MOZ_ASSERT(result != DenseElementResult::Incomplete); if (result == DenseElementResult::Failure) return false; /* Steps 16.a-b. */ - SetAnyBoxedOrUnboxedInitializedLength(cx, obj, len + itemCount - actualDeleteCount); + SetInitializedLength(cx, obj.as(), len + itemCount - actualDeleteCount); } else { RootedValue fromValue(cx); for (double k = len - actualDeleteCount; k > actualStart; k--) { @@ -2824,7 +2783,7 @@ SliceSlowly(JSContext* cx, HandleObject obj, HandleObject receiver, } static bool -SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleObject result) +SliceSparse(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end, HandleArrayObject result) { MOZ_ASSERT(begin <= end); @@ -2874,26 +2833,28 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint32_t length, uint32_t be begin = end; if (!ObjectMayHaveExtraIndexedProperties(obj)) { - size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); + size_t initlen = obj->as().getDenseInitializedLength(); size_t count = 0; if (initlen > begin) count = Min(initlen - begin, end - begin); - RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count)); + RootedArrayObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count)); if (!narr) return false; - SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin); + + MOZ_ASSERT(count >= narr->as().length()); + narr->as().setLength(cx, count); if (count) { DebugOnly result = - CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count); + CopyDenseElements(cx, &narr->as(), &obj->as(), 0, begin, count); MOZ_ASSERT(result.value == DenseElementResult::Success); } arr.set(narr); return true; } - RootedObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); + RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin)); if (!narr) return false; @@ -3010,11 +2971,10 @@ js::array_slice(JSContext* cx, unsigned argc, Value* vp) return true; } -template -DenseElementResult -ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t endArg, JSObject* result) +static bool +ArraySliceDenseKernel(JSContext* cx, ArrayObject* arr, int32_t beginArg, int32_t endArg, ArrayObject* result) { - int32_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + int32_t length = arr->length(); uint32_t begin = NormalizeSliceTerm(beginArg, length); uint32_t end = NormalizeSliceTerm(endArg, length); @@ -3022,33 +2982,33 @@ ArraySliceDenseKernel(JSContext* cx, JSObject* obj, int32_t beginArg, int32_t en if (begin > end) begin = end; - size_t initlen = GetBoxedOrUnboxedInitializedLength(obj); + size_t initlen = arr->getDenseInitializedLength(); + size_t count = Min(initlen - begin, end - begin); if (initlen > begin) { - size_t count = Min(initlen - begin, end - begin); if (count) { - DenseElementResult rv = EnsureBoxedOrUnboxedDenseElements(cx, result, count); - if (rv != DenseElementResult::Success) - return rv; - CopyBoxedOrUnboxedDenseElements(cx, result, obj, 0, begin, count); + if (!result->ensureElements(cx, count)) + return false; + CopyDenseElements(cx, &result->as(), &arr->as(), 0, begin, count); } } - SetAnyBoxedOrUnboxedArrayLength(cx, result, end - begin); - return DenseElementResult::Success; -} + MOZ_ASSERT(count >= result->length()); + result->setLength(cx, count); -DefineBoxedOrUnboxedFunctor5(ArraySliceDenseKernel, - JSContext*, JSObject*, int32_t, int32_t, JSObject*); + return true; +} JSObject* js::array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, HandleObject result) { if (result && IsArraySpecies(cx, obj)) { - ArraySliceDenseKernelFunctor functor(cx, obj, begin, end, result); - DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result); - MOZ_ASSERT(rv != DenseElementResult::Incomplete); - return rv == DenseElementResult::Success ? result : nullptr; + if (!ArraySliceDenseKernel(cx, &obj->as(), begin, end, + &result->as())) + { + return nullptr; + } + return result; } // Slower path if the JIT wasn't able to allocate an object inline. @@ -3079,7 +3039,7 @@ array_isArray(JSContext* cx, unsigned argc, Value* vp) static bool ArrayFromCallArgs(JSContext* cx, CallArgs& args, HandleObject proto = nullptr) { - JSObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); + ArrayObject* obj = NewCopiedArrayForCallingAllocationSite(cx, args.array(), args.length(), proto); if (!obj) return false; @@ -3244,7 +3204,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor) } } - JSObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); + ArrayObject* obj = NewPartlyAllocatedArrayForCallingAllocationSite(cx, length, proto); if (!obj) return false; @@ -3270,7 +3230,7 @@ js::array_construct(JSContext* cx, unsigned argc, Value* vp) return ArrayConstructorImpl(cx, args, /* isConstructor = */ false); } -JSObject* +ArrayObject* js::ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt) { if (lengthInt < 0) { @@ -3565,7 +3525,7 @@ js::NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSOb return arr; } -JSObject* +ArrayObject* js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap) { MOZ_ASSERT(!gc::IsInsideNursery(templateObject)); @@ -3578,30 +3538,21 @@ js::NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc return arr; } -// Return a new boxed or unboxed array with the specified length and allocated -// capacity (up to maxLength), using the specified group if possible. If the -// specified group cannot be used, ensure that the created array at least has -// the given [[Prototype]]. +// Return a new array with the specified length and allocated capacity (up to +// maxLength), using the specified group if possible. If the specified group +// cannot be used, ensure that the created array at least has the given +// [[Prototype]]. template -static inline JSObject* +static inline ArrayObject* NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind = GenericObject) { MOZ_ASSERT(newKind != SingletonObject); - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - - if (group->shouldPreTenure() || group->maybePreliminaryObjects()) + if (group->shouldPreTenure()) newKind = TenuredObject; RootedObject proto(cx, group->proto().toObject()); - if (group->maybeUnboxedLayout()) { - if (length > UnboxedArrayObject::MaximumCapacity) - return NewArray(cx, length, proto, newKind); - return UnboxedArrayObject::create(cx, group, length, newKind, maxLength); - } - ArrayObject* res = NewArray(cx, length, proto, newKind); if (!res) return nullptr; @@ -3613,20 +3564,17 @@ NewArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length if (res->length() > INT32_MAX) res->setLength(cx, res->length()); - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(res); - return res; } -JSObject* +ArrayObject* js::NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind) { return NewArrayTryUseGroup(cx, group, length, newKind); } -JSObject* +ArrayObject* js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length) { return NewArrayTryUseGroup(cx, group, length); @@ -3635,16 +3583,13 @@ js::NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup g // Return a new array with the default prototype and specified allocated // capacity and length. If possible, try to reuse the group of the input // object. The resulting array will either reuse the input object's group or -// will have unknown property types. Additionally, the result will have the -// same boxed/unboxed elements representation as the input object, unless -// |length| is larger than the input object's initialized length (in which case -// UnboxedArrayObject::MaximumCapacity might be exceeded). +// will have unknown property types. template -static inline JSObject* +static inline ArrayObject* NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, NewObjectKind newKind = GenericObject) { - if (!obj->is() && !obj->is()) + if (!obj->is()) return NewArray(cx, length, nullptr, newKind); if (obj->staticPrototype() != cx->global()->maybeGetArrayPrototype()) @@ -3657,20 +3602,20 @@ NewArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, return NewArrayTryUseGroup(cx, group, length, newKind); } -JSObject* +ArrayObject* js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, NewObjectKind newKind) { return NewArrayTryReuseGroup(cx, obj, length, newKind); } -JSObject* +ArrayObject* js::NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length) { return NewArrayTryReuseGroup(cx, obj, length); } -JSObject* +ArrayObject* js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, NewObjectKind newKind) { @@ -3680,7 +3625,7 @@ js::NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, return NewArrayTryUseGroup(cx, group, length, newKind); } -JSObject* +ArrayObject* js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto) { RootedObjectGroup group(cx, ObjectGroup::callingAllocationSiteGroup(cx, JSProto_Array, proto)); @@ -3689,68 +3634,23 @@ js::NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length return NewArrayTryUseGroup(cx, group, length); } -bool -js::MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group, - const Value* vp, size_t length) -{ - static const size_t EagerPreliminaryObjectAnalysisThreshold = 800; - - // Force analysis to see if an unboxed array can be used when making a - // sufficiently large array, to avoid excessive analysis and copying later - // on. If this is the first array of its group that is being created, first - // make a dummy array with the initial elements of the array we are about - // to make, so there is some basis for the unboxed array analysis. - if (length > EagerPreliminaryObjectAnalysisThreshold) { - if (PreliminaryObjectArrayWithTemplate* objects = group->maybePreliminaryObjects()) { - if (objects->empty()) { - size_t nlength = Min(length, 100); - JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, nlength); - if (!obj) - return false; - DebugOnly result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, nlength, - ShouldUpdateTypes::Update); - MOZ_ASSERT(result.value == DenseElementResult::Success); - } - objects->maybeAnalyze(cx, group, /* forceAnalyze = */ true); - } - } - return true; -} - -JSObject* +ArrayObject* js::NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, const Value* vp, size_t length, NewObjectKind newKind, ShouldUpdateTypes updateTypes) { - if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length)) - return nullptr; - - JSObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind); + ArrayObject* obj = NewFullyAllocatedArrayTryUseGroup(cx, group, length, newKind); if (!obj) return nullptr; - DenseElementResult result = - SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, updateTypes); + DenseElementResult result = obj->setOrExtendDenseElements(cx->asJSContext(), 0, vp, length, updateTypes); if (result == DenseElementResult::Failure) return nullptr; - if (result == DenseElementResult::Success) - return obj; - - MOZ_ASSERT(obj->is()); - if (!UnboxedArrayObject::convertToNative(cx->asJSContext(), obj)) - return nullptr; - - result = SetOrExtendBoxedOrUnboxedDenseElements(cx, obj, 0, vp, length, - updateTypes); - MOZ_ASSERT(result != DenseElementResult::Incomplete); - if (result == DenseElementResult::Failure) - return nullptr; - + MOZ_ASSERT(result == DenseElementResult::Success); return obj; } -JSObject* +ArrayObject* js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto /* = nullptr */) { diff --git a/js/src/jsarray.h b/js/src/jsarray.h index e22cde881..ec2e4f514 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -72,49 +72,37 @@ extern ArrayObject* NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject); /* Create a dense array with the same copy-on-write elements as another object. */ -extern JSObject* +extern ArrayObject* NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap); -// The methods below can create either boxed or unboxed arrays. - -extern JSObject* +extern ArrayObject* NewFullyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* +extern ArrayObject* NewPartlyAllocatedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, size_t length); -extern JSObject* +extern ArrayObject* NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* +extern ArrayObject* NewPartlyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length); -extern JSObject* +extern ArrayObject* NewFullyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, NewObjectKind newKind = GenericObject); -extern JSObject* +extern ArrayObject* NewPartlyAllocatedArrayForCallingAllocationSite(JSContext* cx, size_t length, HandleObject proto); -enum class ShouldUpdateTypes -{ - Update, - DontUpdate -}; - -extern bool -MaybeAnalyzeBeforeCreatingLargeArray(ExclusiveContext* cx, HandleObjectGroup group, - const Value* vp, size_t length); - -extern JSObject* +extern ArrayObject* NewCopiedArrayTryUseGroup(ExclusiveContext* cx, HandleObjectGroup group, const Value* vp, size_t length, NewObjectKind newKind = GenericObject, ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); -extern JSObject* +extern ArrayObject* NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto = nullptr); @@ -129,13 +117,6 @@ NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleV extern bool WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index); -/* - * Canonicalize |vp| to a uint32_t value potentially suitable for use as an - * array length. - */ -extern bool -CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* canonicalized); - extern bool GetLengthProperty(JSContext* cx, HandleObject obj, uint32_t* lengthp); @@ -172,7 +153,7 @@ extern bool array_join(JSContext* cx, unsigned argc, js::Value* vp); extern void -ArrayShiftMoveElements(JSObject* obj); +ArrayShiftMoveElements(NativeObject* obj); extern bool array_shift(JSContext* cx, unsigned argc, js::Value* vp); @@ -202,7 +183,7 @@ array_splice(JSContext* cx, unsigned argc, js::Value* vp); extern bool NewbornArrayPush(JSContext* cx, HandleObject obj, const Value& v); -extern JSObject* +extern ArrayObject* ArrayConstructorOneArg(JSContext* cx, HandleObjectGroup group, int32_t lengthInt); #ifdef DEBUG diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index a48bb0ffe..6024a1768 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -112,13 +112,6 @@ JSCompartment::~JSCompartment() js_delete(nonSyntacticLexicalEnvironments_), js_free(enumerators); -#ifdef DEBUG - // Avoid assertion destroying the unboxed layouts list if the embedding - // leaked GC things. - if (!rt->gc.shutdownCollectedEverything()) - unboxedLayouts.clear(); -#endif - runtime_->numCompartments--; } diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index 98c8fe200..83c15da3b 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -529,9 +529,6 @@ struct JSCompartment // table manages references from such typed objects to their buffers. js::ObjectWeakMap* lazyArrayBuffers; - // All unboxed layouts in the compartment. - mozilla::LinkedList unboxedLayouts; - // WebAssembly state for the compartment. js::wasm::Compartment wasm; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index f5cd56a9b..5baba0beb 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -269,9 +269,9 @@ js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClass* cls) if (MOZ_UNLIKELY(obj->is())) return Proxy::getBuiltinClass(cx, obj, cls); - if (obj->is() || obj->is()) + if (obj->is()) *cls = ESClass::Object; - else if (obj->is() || obj->is()) + else if (obj->is()) *cls = ESClass::Array; else if (obj->is()) *cls = ESClass::Number; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 194468c5d..b2ee8d67b 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6190,12 +6190,6 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target) for (auto group = source->zone()->cellIter(); !group.done(); group.next()) { group->setGeneration(target->zone()->types.generation); group->compartment_ = target; - - // Remove any unboxed layouts from the list in the off thread - // compartment. These do not need to be reinserted in the target - // compartment's list, as the list is not required to be complete. - if (UnboxedLayout* layout = group->maybeUnboxedLayoutDontCheckGeneration()) - layout->detachFromCompartment(); } // Fixup zone pointers in source's zone to refer to target's zone. diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index c1ae5dc15..004c7fc0d 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -157,8 +157,11 @@ SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) } static bool -EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe& ht, - AutoIdVector* props, Handle unboxed = nullptr) +EnumerateNativeProperties(JSContext* cx, + HandleNativeObject pobj, + unsigned flags, + Maybe& ht, + AutoIdVector* props) { bool enumerateSymbols; if (flags & JSITER_SYMBOLSONLY) { @@ -220,16 +223,6 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags return false; } - if (unboxed) { - // If |unboxed| is set then |pobj| is the expando for an unboxed - // plain object we are enumerating. Add the unboxed properties - // themselves here since they are all property names that were - // given to the object before any of the expando's properties. - MOZ_ASSERT(pobj->is()); - if (!EnumerateExtraProperties(cx, unboxed, flags, ht, props)) - return false; - } - size_t initialLength = props->length(); /* Collect all unique property names from this object's shape. */ @@ -355,22 +348,12 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) do { if (pobj->getOpsEnumerate()) { - if (pobj->is() && pobj->as().maybeExpando()) { - // Special case unboxed objects with an expando object. - RootedNativeObject expando(cx, pobj->as().maybeExpando()); - if (!EnumerateNativeProperties(cx, expando, flags, ht, props, - pobj.as())) - { - return false; - } - } else { - if (!EnumerateExtraProperties(cx, pobj, flags, ht, props)) - return false; + if (!EnumerateExtraProperties(cx, pobj, flags, ht, props)) + return false; - if (pobj->isNative()) { - if (!EnumerateNativeProperties(cx, pobj.as(), flags, ht, props)) - return false; - } + if (pobj->isNative()) { + if (!EnumerateNativeProperties(cx, pobj.as(), flags, ht, props)) + return false; } } else if (pobj->isNative()) { // Give the object a chance to resolve all lazy properties @@ -785,11 +768,6 @@ CanCompareIterableObjectToCache(JSObject* obj) { if (obj->isNative()) return obj->as().hasEmptyElements(); - if (obj->is()) { - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - return expando->hasEmptyElements(); - return true; - } return false; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b17c845bb..2364f707e 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -43,6 +43,7 @@ #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "gc/Policy.h" +#include "gc/StoreBuffer-inl.h" #include "jit/BaselineJIT.h" #include "js/MemoryMetrics.h" #include "js/Proxy.h" @@ -869,9 +870,6 @@ static inline JSObject* CreateThisForFunctionWithGroup(JSContext* cx, HandleObjectGroup group, NewObjectKind newKind) { - if (group->maybeUnboxedLayout() && newKind != SingletonObject) - return UnboxedPlainObject::create(cx, group, newKind); - if (TypeNewScript* newScript = group->newScript()) { if (newScript->analyzed()) { // The definite properties analysis has been performed for this @@ -1145,19 +1143,18 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle proto) } static bool -GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle> values) +GetScriptArrayObjectElements(JSContext* cx, HandleArrayObject arr, MutableHandle> values) { - MOZ_ASSERT(!obj->isSingleton()); - MOZ_ASSERT(obj->is() || obj->is()); - MOZ_ASSERT(!obj->isIndexed()); + MOZ_ASSERT(!arr->isSingleton()); + MOZ_ASSERT(!arr->isIndexed()); - size_t length = GetAnyBoxedOrUnboxedArrayLength(obj); + size_t length = arr->length(); if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length)) return false; - size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(obj); + size_t initlen = arr->getDenseInitializedLength(); for (size_t i = 0; i < initlen; i++) - values[i].set(GetAnyBoxedOrUnboxedDenseElement(obj, i)); + values[i].set(arr->getDenseElement(i)); return true; } @@ -1166,46 +1163,27 @@ static bool GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj, MutableHandle properties) { - if (obj->is()) { - PlainObject* nobj = &obj->as(); + MOZ_ASSERT(obj->is()); + PlainObject* nobj = &obj->as(); - if (!properties.appendN(IdValuePair(), nobj->slotSpan())) - return false; + if (!properties.appendN(IdValuePair(), nobj->slotSpan())) + return false; - for (Shape::Range r(nobj->lastProperty()); !r.empty(); r.popFront()) { - Shape& shape = r.front(); - MOZ_ASSERT(shape.isDataDescriptor()); - uint32_t slot = shape.slot(); - properties[slot].get().id = shape.propid(); - properties[slot].get().value = nobj->getSlot(slot); - } - - for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) { - Value v = nobj->getDenseElement(i); - if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) - return false; - } - - return true; + for (Shape::Range r(nobj->lastProperty()); !r.empty(); r.popFront()) { + Shape& shape = r.front(); + MOZ_ASSERT(shape.isDataDescriptor()); + uint32_t slot = shape.slot(); + properties[slot].get().id = shape.propid(); + properties[slot].get().value = nobj->getSlot(slot); } - if (obj->is()) { - UnboxedPlainObject* nobj = &obj->as(); - - const UnboxedLayout& layout = nobj->layout(); - if (!properties.appendN(IdValuePair(), layout.properties().length())) + for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) { + Value v = nobj->getDenseElement(i); + if (!v.isMagic(JS_ELEMENTS_HOLE) && !properties.append(IdValuePair(INT_TO_JSID(i), v))) return false; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - properties[i].get().id = NameToId(property.name); - properties[i].get().value = nobj->getValue(property); - } - - return true; } - MOZ_CRASH("Bad object kind"); + return true; } static bool @@ -1227,13 +1205,13 @@ js::DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKin /* NB: Keep this in sync with XDRObjectLiteral. */ MOZ_ASSERT_IF(obj->isSingleton(), cx->compartment()->behaviors().getSingletonsAsTemplates()); - MOZ_ASSERT(obj->is() || obj->is() || - obj->is() || obj->is()); + MOZ_ASSERT(obj->is() || + obj->is()); MOZ_ASSERT(newKind != SingletonObject); - if (obj->is() || obj->is()) { + if (obj->is()) { Rooted> values(cx, GCVector(cx)); - if (!GetScriptArrayObjectElements(cx, obj, &values)) + if (!GetScriptArrayObjectElements(cx, obj.as(), &values)) return nullptr; // Deep clone any elements. @@ -1347,10 +1325,8 @@ js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj) { if (mode == XDR_ENCODE) { MOZ_ASSERT(obj->is() || - obj->is() || - obj->is() || - obj->is()); - isArray = (obj->is() || obj->is()) ? 1 : 0; + obj->is()); + isArray = obj->is() ? 1 : 0; } if (!xdr->codeUint32(&isArray)) @@ -1362,8 +1338,11 @@ js::XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj) if (isArray) { Rooted> values(cx, GCVector(cx)); - if (mode == XDR_ENCODE && !GetScriptArrayObjectElements(cx, obj, &values)) - return false; + if (mode == XDR_ENCODE) { + RootedArrayObject arr(cx, &obj->as()); + if (!GetScriptArrayObjectElements(cx, arr, &values)) + return false; + } uint32_t initialized; if (mode == XDR_ENCODE) @@ -2333,16 +2312,6 @@ js::LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** // us the resolve hook won't define a property with this id. if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) return false; - } else if (obj->is()) { - if (obj->as().containsUnboxedOrExpandoProperty(cx, id)) { - MarkNonNativePropertyFound(propp); - return true; - } - } else if (obj->is()) { - if (obj->as().containsProperty(cx, id)) { - MarkNonNativePropertyFound(propp); - return true; - } } else if (obj->is()) { if (obj->as().typeDescr().hasProperty(cx->names(), id)) { MarkNonNativePropertyFound(propp); @@ -2590,11 +2559,6 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object break; } - // Convert unboxed objects to their native representations before changing - // their prototype/group, as they depend on the group for their layout. - if (!MaybeConvertUnboxedObjectToNative(cx, obj)) - return false; - Rooted taggedProto(cx, TaggedProto(proto)); if (!SetClassAndProto(cx, obj, obj->getClass(), taggedProto)) return false; @@ -2618,9 +2582,6 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, I if (!obj->nonProxyIsExtensible()) return result.succeed(); - if (!MaybeConvertUnboxedObjectToNative(cx, obj)) - return false; - // Force lazy properties to be resolved. AutoIdVector props(cx); if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) @@ -3714,22 +3675,6 @@ JSObject::allocKindForTenure(const js::Nursery& nursery) const if (IsProxy(this)) return as().allocKindForTenure(); - // Unboxed plain objects are sized according to the data they store. - if (is()) { - size_t nbytes = as().layoutDontCheckGeneration().size(); - return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes); - } - - // Unboxed arrays use inline data if their size is small enough. - if (is()) { - const UnboxedArrayObject* nobj = &as(); - size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() + - nobj->capacity() * nobj->elementSize(); - if (nbytes <= JSObject::MAX_BYTE_SIZE) - return GetGCObjectKindForBytes(nbytes); - return AllocKind::OBJECT0; - } - // Inlined typed objects are followed by their data, so make sure we copy // it all over to the new object. if (is()) { diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 6be4d0d28..b1d817bca 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -32,21 +32,6 @@ #include "vm/ShapedObject-inl.h" #include "vm/TypeInference-inl.h" -namespace js { - -// This is needed here for ensureShape() below. -inline bool -MaybeConvertUnboxedObjectToNative(ExclusiveContext* cx, JSObject* obj) -{ - if (obj->is()) - return UnboxedPlainObject::convertToNative(cx->asJSContext(), obj); - if (obj->is()) - return UnboxedArrayObject::convertToNative(cx->asJSContext(), obj); - return true; -} - -} // namespace js - inline js::Shape* JSObject::maybeShape() const { @@ -59,8 +44,6 @@ JSObject::maybeShape() const inline js::Shape* JSObject::ensureShape(js::ExclusiveContext* cx) { - if (!js::MaybeConvertUnboxedObjectToNative(cx, this)) - return nullptr; js::Shape* shape = maybeShape(); MOZ_ASSERT(shape); return shape; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 7765b1197..d7db5129d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2369,7 +2369,7 @@ js::str_replace_string_raw(JSContext* cx, HandleString string, HandleString patt } // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18. -static JSObject* +static ArrayObject* SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearString sep, HandleObjectGroup group) { @@ -2466,7 +2466,7 @@ SplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleLinearS } // Fast-path for splitting a string into a character array via split(""). -static JSObject* +static ArrayObject* CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObjectGroup group) { size_t strLength = str->length(); @@ -2491,7 +2491,7 @@ CharSplitHelper(JSContext* cx, HandleLinearString str, uint32_t limit, HandleObj } // ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18. -JSObject* +ArrayObject* js::str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit) { diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 38fbfa85e..68175c826 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -465,7 +465,7 @@ FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote) return res; } -JSObject* +ArrayObject* str_split_string(JSContext* cx, HandleObjectGroup group, HandleString str, HandleString sep, uint32_t limit); diff --git a/js/src/moz.build b/js/src/moz.build index 888741138..eb30866c8 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -355,7 +355,6 @@ UNIFIED_SOURCES += [ 'vm/UbiNode.cpp', 'vm/UbiNodeCensus.cpp', 'vm/UbiNodeShortestPaths.cpp', - 'vm/UnboxedObject.cpp', 'vm/Unicode.cpp', 'vm/Value.cpp', 'vm/WeakMapPtr.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8d144417a..617b5e902 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -320,7 +320,6 @@ static bool enableIon = false; static bool enableAsmJS = false; static bool enableWasm = false; static bool enableNativeRegExp = false; -static bool enableUnboxedArrays = false; static bool enableSharedMemory = SHARED_MEMORY_DEFAULT; static bool enableWasmAlwaysBaseline = false; static bool enableArrayProtoValues = true; @@ -7260,7 +7259,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op) enableAsmJS = !op.getBoolOption("no-asmjs"); enableWasm = !op.getBoolOption("no-wasm"); enableNativeRegExp = !op.getBoolOption("no-native-regexp"); - enableUnboxedArrays = op.getBoolOption("unboxed-arrays"); enableWasmAlwaysBaseline = op.getBoolOption("wasm-always-baseline"); enableArrayProtoValues = !op.getBoolOption("no-array-proto-values"); @@ -7270,15 +7268,11 @@ SetContextOptions(JSContext* cx, const OptionParser& op) .setWasm(enableWasm) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setNativeRegExp(enableNativeRegExp) - .setUnboxedArrays(enableUnboxedArrays) .setArrayProtoValues(enableArrayProtoValues); if (op.getBoolOption("wasm-check-bce")) jit::JitOptions.wasmAlwaysCheckBounds = true; - if (op.getBoolOption("no-unboxed-objects")) - jit::JitOptions.disableUnboxedObjects = true; - if (const char* str = op.getStringOption("cache-ir-stubs")) { if (strcmp(str, "on") == 0) jit::JitOptions.disableCacheIR = false; @@ -7542,7 +7536,6 @@ SetWorkerContextOptions(JSContext* cx) .setWasm(enableWasm) .setWasmAlwaysBaseline(enableWasmAlwaysBaseline) .setNativeRegExp(enableNativeRegExp) - .setUnboxedArrays(enableUnboxedArrays) .setArrayProtoValues(enableArrayProtoValues); cx->setOffthreadIonCompilationEnabled(offthreadCompilation); cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps; @@ -7712,8 +7705,6 @@ main(int argc, char** argv, char** envp) || !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") || !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation") || !op.addBoolOption('\0', "no-native-regexp", "Disable native regexp compilation") - || !op.addBoolOption('\0', "no-unboxed-objects", "Disable creating unboxed plain objects") - || !op.addBoolOption('\0', "unboxed-arrays", "Allow creating unboxed arrays") || !op.addBoolOption('\0', "wasm-always-baseline", "Enable wasm baseline compiler when possible") || !op.addBoolOption('\0', "wasm-check-bce", "Always generate wasm bounds check, even redundant ones.") || !op.addBoolOption('\0', "no-array-proto-values", "Remove Array.prototype.values") diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 5f476c4ff..a2c8e220a 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -22,7 +22,6 @@ #include "vm/EnvironmentObject-inl.h" #include "vm/Stack-inl.h" #include "vm/String-inl.h" -#include "vm/UnboxedObject-inl.h" namespace js { @@ -337,14 +336,10 @@ InitGlobalLexicalOperation(JSContext* cx, LexicalEnvironmentObject* lexicalEnvAr inline bool InitPropertyOperation(JSContext* cx, JSOp op, HandleObject obj, HandleId id, HandleValue rhs) { - if (obj->is() || obj->is()) { - unsigned propAttrs = GetInitDataPropAttrs(op); - return NativeDefineProperty(cx, obj.as(), id, rhs, nullptr, nullptr, - propAttrs); - } - - MOZ_ASSERT(obj->as().layout().lookup(id)); - return PutProperty(cx, obj, id, rhs, false); + MOZ_ASSERT(obj->is() || obj->is()); + unsigned propAttrs = GetInitDataPropAttrs(op); + return NativeDefineProperty(cx, obj.as(), id, rhs, + nullptr, nullptr, propAttrs); } inline bool @@ -598,7 +593,7 @@ InitArrayElemOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, uint32_t JSOp op = JSOp(*pc); MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC); - MOZ_ASSERT(obj->is() || obj->is()); + MOZ_ASSERT(obj->is()); if (op == JSOP_INITELEM_INC && index == INT32_MAX) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SPREAD_TOO_LARGE); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index e6d6630c4..0f83c3435 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1916,6 +1916,7 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) +CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) @@ -3636,7 +3637,6 @@ CASE(JSOP_NEWINIT) END_CASE(JSOP_NEWINIT) CASE(JSOP_NEWARRAY) -CASE(JSOP_SPREADCALLARRAY) { uint32_t length = GET_UINT32(REGS.pc); JSObject* obj = NewArrayOperation(cx, script, REGS.pc, length); @@ -4111,7 +4111,7 @@ CASE(JSOP_INITHOMEOBJECT) /* Load the home object */ ReservedRooted obj(&rootObject0); obj = ®S.sp[int(-2 - skipOver)].toObject(); - MOZ_ASSERT(obj->is() || obj->is() || obj->is()); + MOZ_ASSERT(obj->is() || obj->is()); func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj)); } @@ -4927,18 +4927,13 @@ js::NewObjectOperation(JSContext* cx, HandleScript script, jsbytecode* pc, return nullptr; if (group->maybePreliminaryObjects()) { group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - if (group->maybeUnboxedLayout()) - group->maybeUnboxedLayout()->setAllocationSite(script, pc); } if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return UnboxedPlainObject::create(cx, group, newKind); } - RootedObject obj(cx); + RootedPlainObject obj(cx); if (*pc == JSOP_NEWOBJECT) { RootedPlainObject baseObject(cx, &script->getObject(pc)->as()); @@ -4975,11 +4970,6 @@ js::NewObjectOperationWithTemplate(JSContext* cx, HandleObject templateObject) NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; - if (templateObject->group()->maybeUnboxedLayout()) { - RootedObjectGroup group(cx, templateObject->group()); - return UnboxedPlainObject::create(cx, group, newKind); - } - JSObject* obj = CopyInitializerObject(cx, templateObject.as(), newKind); if (!obj) return nullptr; @@ -5006,9 +4996,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32 if (group->shouldPreTenure() || group->maybePreliminaryObjects()) newKind = TenuredObject; - - if (group->maybeUnboxedLayout()) - return UnboxedArrayObject::create(cx, group, length, newKind); } ArrayObject* obj = NewDenseFullyAllocatedArray(cx, length, nullptr, newKind); @@ -5019,9 +5006,6 @@ js::NewArrayOperation(JSContext* cx, HandleScript script, jsbytecode* pc, uint32 MOZ_ASSERT(obj->isSingleton()); } else { obj->setGroup(group); - - if (PreliminaryObjectArray* preliminaryObjects = group->maybePreliminaryObjects()) - preliminaryObjects->registerNewObject(obj); } return obj; @@ -5034,12 +5018,6 @@ js::NewArrayOperationWithTemplate(JSContext* cx, HandleObject templateObject) NewObjectKind newKind = templateObject->group()->shouldPreTenure() ? TenuredObject : GenericObject; - if (templateObject->is()) { - uint32_t length = templateObject->as().length(); - RootedObjectGroup group(cx, templateObject->group()); - return UnboxedArrayObject::create(cx, group, length, newKind); - } - ArrayObject* obj = NewDenseFullyAllocatedArray(cx, templateObject->as().length(), nullptr, newKind); if (!obj) diff --git a/js/src/vm/JSONParser.cpp b/js/src/vm/JSONParser.cpp index 01883bb15..e50da3bc4 100644 --- a/js/src/vm/JSONParser.cpp +++ b/js/src/vm/JSONParser.cpp @@ -606,8 +606,8 @@ JSONParserBase::finishArray(MutableHandleValue vp, ElementVector& elements) { MOZ_ASSERT(&elements == &stack.back().elements()); - JSObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(), - GenericObject); + ArrayObject* obj = ObjectGroup::newArrayObject(cx, elements.begin(), elements.length(), + GenericObject); if (!obj) return false; diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 48a42a8db..052a3385c 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -235,6 +235,38 @@ NativeObject::ensureDenseElements(ExclusiveContext* cx, uint32_t index, uint32_t return DenseElementResult::Success; } +inline DenseElementResult +NativeObject::setOrExtendDenseElements(JSContext* cx, uint32_t start, const Value* vp, + uint32_t count, + ShouldUpdateTypes updateTypes) +{ + if (denseElementsAreFrozen()) + return DenseElementResult::Incomplete; + + if (is() && + !as().lengthIsWritable() && + start + count >= as().length()) + { + return DenseElementResult::Incomplete; + } + + DenseElementResult result = ensureDenseElements(cx, start, count); + if (result != DenseElementResult::Success) + return result; + + if (is() && start + count >= as().length()) + as().setLengthInt32(start + count); + + if (updateTypes == ShouldUpdateTypes::DontUpdate && !shouldConvertDoubleElements()) { + copyDenseElements(start, vp, count); + } else { + for (size_t i = 0; i < count; i++) + setDenseElementWithType(cx, start + i, vp[i]); + } + + return DenseElementResult::Success; +} + inline Value NativeObject::getDenseOrTypedArrayElement(uint32_t idx) { diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index 21f73f4a9..a3f28653a 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -390,33 +390,6 @@ NativeObject::setLastPropertyMakeNonNative(Shape* shape) shape_ = shape; } -void -NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape) -{ - MOZ_ASSERT(getClass()->isNative()); - MOZ_ASSERT(shape->getObjectClass()->isNative()); - MOZ_ASSERT(!shape->inDictionary()); - - // This method is used to convert unboxed objects into native objects. In - // this case, the shape_ field was previously used to store other data and - // this should be treated as an initialization. - shape_.init(shape); - - slots_ = nullptr; - elements_ = emptyObjectElements; - - size_t oldSpan = shape->numFixedSlots(); - size_t newSpan = shape->slotSpan(); - - initializeSlotRange(0, oldSpan); - - // A failure at this point will leave the object as a mutant, and we - // can't recover. - AutoEnterOOMUnsafeRegion oomUnsafe; - if (oldSpan != newSpan && !updateSlotsForSpan(cx, oldSpan, newSpan)) - oomUnsafe.crash("NativeObject::setLastPropertyMakeNative"); -} - bool NativeObject::setSlotSpan(ExclusiveContext* cx, uint32_t span) { diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 4dbc167ab..d9d8b8aec 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -339,16 +339,19 @@ IsObjectValueInCompartment(const Value& v, JSCompartment* comp); #endif // Operations which change an object's dense elements can either succeed, fail, -// or be unable to complete. For native objects, the latter is used when the -// object's elements must become sparse instead. The enum below is used for -// such operations, and for similar operations on unboxed arrays and methods -// that work on both kinds of objects. +// or be unable to complete. The latter is used when the object's elements must +// become sparse instead. The enum below is used for such operations. enum class DenseElementResult { Failure, Success, Incomplete }; +enum class ShouldUpdateTypes { + Update, + DontUpdate +}; + /* * NativeObject specifies the internal implementation of a native object. * @@ -467,11 +470,6 @@ class NativeObject : public ShapedObject // that are (temporarily) inconsistent. void setLastPropertyMakeNonNative(Shape* shape); - // As for setLastProperty(), but changes the class associated with the - // object to a native one. The object's type has already been changed, and - // this brings the shape into sync with it. - void setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape); - // Newly-created TypedArrays that map a SharedArrayBuffer are // marked as shared by giving them an ObjectElements that has the // ObjectElements::SHARED_MEMORY flag set. @@ -1147,6 +1145,10 @@ class NativeObject : public ShapedObject elementsRangeWriteBarrierPost(dstStart, count); } + inline DenseElementResult + setOrExtendDenseElements(JSContext* cx, uint32_t start, const Value* vp, uint32_t count, + ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); + bool shouldConvertDoubleElements() { return getElementsHeader()->shouldConvertDoubleElements(); } diff --git a/js/src/vm/ObjectGroup-inl.h b/js/src/vm/ObjectGroup-inl.h index 9074f4d97..d41343be6 100644 --- a/js/src/vm/ObjectGroup-inl.h +++ b/js/src/vm/ObjectGroup-inl.h @@ -108,20 +108,6 @@ ObjectGroup::maybePreliminaryObjects() return maybePreliminaryObjectsDontCheckGeneration(); } -inline UnboxedLayout* -ObjectGroup::maybeUnboxedLayout() -{ - maybeSweep(nullptr); - return maybeUnboxedLayoutDontCheckGeneration(); -} - -inline UnboxedLayout& -ObjectGroup::unboxedLayout() -{ - maybeSweep(nullptr); - return unboxedLayoutDontCheckGeneration(); -} - } // namespace js #endif /* vm_ObjectGroup_inl_h */ diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index 46159a972..676792379 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -18,11 +18,10 @@ #include "vm/ArrayObject.h" #include "vm/Shape.h" #include "vm/TaggedProto.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" -#include "vm/UnboxedObject-inl.h" +#include "vm/NativeObject-inl.h" using namespace js; @@ -56,7 +55,6 @@ ObjectGroup::finalize(FreeOp* fop) if (newScriptDontCheckGeneration()) newScriptDontCheckGeneration()->clear(); fop->delete_(newScriptDontCheckGeneration()); - fop->delete_(maybeUnboxedLayoutDontCheckGeneration()); if (maybePreliminaryObjectsDontCheckGeneration()) maybePreliminaryObjectsDontCheckGeneration()->clear(); fop->delete_(maybePreliminaryObjectsDontCheckGeneration()); @@ -83,8 +81,6 @@ ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const size_t n = 0; if (TypeNewScript* newScript = newScriptDontCheckGeneration()) n += newScript->sizeOfIncludingThis(mallocSizeOf); - if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration()) - n += layout->sizeOfIncludingThis(mallocSizeOf); return n; } @@ -530,8 +526,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp, if (p) { ObjectGroup* group = p->group; MOZ_ASSERT_IF(clasp, group->clasp() == clasp); - MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ || - group->clasp() == &UnboxedPlainObject::class_); + MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_); MOZ_ASSERT(group->proto() == proto); return group; } @@ -774,7 +769,7 @@ GetValueTypeForTable(const Value& v) return type; } -/* static */ JSObject* +/* static */ ArrayObject* ObjectGroup::newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, NewObjectKind newKind, NewArrayKind arrayKind) @@ -838,56 +833,13 @@ ObjectGroup::newArrayObject(ExclusiveContext* cx, AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType); - if (elementType != TypeSet::UnknownType()) { - // Keep track of the initial objects we create with this type. - // If the initial ones have a consistent shape and property types, we - // will try to use an unboxed layout for the group. - PreliminaryObjectArrayWithTemplate* preliminaryObjects = - cx->new_(nullptr); - if (!preliminaryObjects) - return nullptr; - group->setPreliminaryObjects(preliminaryObjects); - } - if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group)) return nullptr; } // The type of the elements being added will already be reflected in type - // information, but make sure when creating an unboxed array that the - // common element type is suitable for the unboxed representation. + // information. ShouldUpdateTypes updateTypes = ShouldUpdateTypes::DontUpdate; - if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length)) - return nullptr; - if (group->maybePreliminaryObjects()) - group->maybePreliminaryObjects()->maybeAnalyze(cx, group); - if (group->maybeUnboxedLayout()) { - switch (group->unboxedLayout().elementType()) { - case JSVAL_TYPE_BOOLEAN: - if (elementType != TypeSet::BooleanType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_INT32: - if (elementType != TypeSet::Int32Type()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_DOUBLE: - if (elementType != TypeSet::Int32Type() && elementType != TypeSet::DoubleType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_STRING: - if (elementType != TypeSet::StringType()) - updateTypes = ShouldUpdateTypes::Update; - break; - case JSVAL_TYPE_OBJECT: - if (elementType != TypeSet::NullType() && !elementType.get().isObjectUnchecked()) - updateTypes = ShouldUpdateTypes::Update; - break; - default: - MOZ_CRASH(); - } - } - return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, updateTypes); } @@ -897,49 +849,15 @@ GiveObjectGroup(ExclusiveContext* cx, JSObject* source, JSObject* target) { MOZ_ASSERT(source->group() != target->group()); - if (!target->is() && !target->is()) - return true; - - if (target->group()->maybePreliminaryObjects()) { - bool force = IsInsideNursery(source); - target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force); - } - - if (target->is()) { - ObjectGroup* sourceGroup = source->group(); - - if (source->is()) { - Shape* shape = target->as().lastProperty(); - if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape)) - return false; - } else if (source->is()) { - source->setGroup(target->group()); - } else { - return true; - } - - if (sourceGroup->maybePreliminaryObjects()) - sourceGroup->maybePreliminaryObjects()->unregisterObject(source); - if (target->group()->maybePreliminaryObjects()) - target->group()->maybePreliminaryObjects()->registerNewObject(source); - - for (size_t i = 0; i < source->as().getDenseInitializedLength(); i++) { - Value v = source->as().getDenseElement(i); - AddTypePropertyId(cx, source->group(), source, JSID_VOID, v); - } - + if (!target->is() || !source->is()) { return true; } - if (target->is()) { - if (!source->is()) - return true; - if (source->as().elementType() != JSVAL_TYPE_INT32) - return true; - if (target->as().elementType() != JSVAL_TYPE_DOUBLE) - return true; + source->setGroup(target->group()); - return source->as().convertInt32ToDouble(cx, target->group()); + for (size_t i = 0; i < source->as().getDenseInitializedLength(); i++) { + Value v = source->as().getDenseElement(i); + AddTypePropertyId(cx, source->group(), source, JSID_VOID, v); } return true; @@ -1048,46 +966,6 @@ js::CombinePlainObjectPropertyTypes(ExclusiveContext* cx, JSObject* newObj, } } } - } else if (newObj->is()) { - const UnboxedLayout& layout = newObj->as().layout(); - const int32_t* traceList = layout.traceList(); - if (!traceList) - return true; - - uint8_t* newData = newObj->as().data(); - uint8_t* oldData = oldObj->as().data(); - - for (; *traceList != -1; traceList++) {} - traceList++; - for (; *traceList != -1; traceList++) { - JSObject* newInnerObj = *reinterpret_cast(newData + *traceList); - JSObject* oldInnerObj = *reinterpret_cast(oldData + *traceList); - - if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj)) - continue; - - if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj)) - return false; - - if (SameGroup(oldInnerObj, newInnerObj)) - continue; - - if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj)) - return false; - - if (SameGroup(oldInnerObj, newInnerObj)) { - for (size_t i = 1; i < ncompare; i++) { - if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) { - uint8_t* otherData = compare[i].toObject().as().data(); - JSObject* otherInnerObj = *reinterpret_cast(otherData + *traceList); - if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) { - if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj)) - return false; - } - } - } - } - } } return true; @@ -1311,12 +1189,6 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_ RootedObjectGroup group(cx, p->value().group); - // Watch for existing groups which now use an unboxed layout. - if (group->maybeUnboxedLayout()) { - MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties); - return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties); - } - // Update property types according to the properties we are about to add. // Do this before we do anything which can GC, which might move or remove // this table entry. @@ -1503,18 +1375,6 @@ ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* } } - if (kind == JSProto_Array && - (JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) && - cx->options().unboxedArrays()) - { - PreliminaryObjectArrayWithTemplate* preliminaryObjects = - cx->new_(nullptr); - if (preliminaryObjects) - res->setPreliminaryObjects(preliminaryObjects); - else - cx->recoverFromOutOfMemory(); - } - if (!table->add(p, key, res)) { ReportOutOfMemory(cx); return nullptr; diff --git a/js/src/vm/ObjectGroup.h b/js/src/vm/ObjectGroup.h index 4e24de9f1..0439b4c21 100644 --- a/js/src/vm/ObjectGroup.h +++ b/js/src/vm/ObjectGroup.h @@ -20,7 +20,6 @@ namespace js { class TypeDescr; -class UnboxedLayout; class PreliminaryObjectArrayWithTemplate; class TypeNewScript; @@ -154,16 +153,6 @@ class ObjectGroup : public gc::TenuredCell // For some plain objects, the addendum stores a PreliminaryObjectArrayWithTemplate. Addendum_PreliminaryObjects, - // When objects in this group have an unboxed representation, the - // addendum stores an UnboxedLayout (which might have a TypeNewScript - // as well, if the group is also constructed using 'new'). - Addendum_UnboxedLayout, - - // If this group is used by objects that have been converted from an - // unboxed representation and/or have the same allocation kind as such - // objects, the addendum points to that unboxed group. - Addendum_OriginalUnboxedGroup, - // When used by typed objects, the addendum stores a TypeDescr. Addendum_TypeDescr }; @@ -225,34 +214,6 @@ class ObjectGroup : public gc::TenuredCell maybePreliminaryObjectsDontCheckGeneration(); } - inline UnboxedLayout* maybeUnboxedLayout(); - inline UnboxedLayout& unboxedLayout(); - - UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const { - if (addendumKind() == Addendum_UnboxedLayout) - return reinterpret_cast(addendum_); - return nullptr; - } - - UnboxedLayout& unboxedLayoutDontCheckGeneration() const { - MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout); - return *maybeUnboxedLayoutDontCheckGeneration(); - } - - void setUnboxedLayout(UnboxedLayout* layout) { - setAddendum(Addendum_UnboxedLayout, layout); - } - - ObjectGroup* maybeOriginalUnboxedGroup() const { - if (addendumKind() == Addendum_OriginalUnboxedGroup) - return reinterpret_cast(addendum_); - return nullptr; - } - - void setOriginalUnboxedGroup(ObjectGroup* group) { - setAddendum(Addendum_OriginalUnboxedGroup, group); - } - TypeDescr* maybeTypeDescr() { // Note: there is no need to sweep when accessing the type descriptor // of an object, as it is strongly held and immutable. @@ -313,9 +274,8 @@ class ObjectGroup : public gc::TenuredCell * that can be read out of that property in actual JS objects. In native * objects, property types account for plain data properties (those with a * slot and no getter or setter hook) and dense elements. In typed objects - * and unboxed objects, property types account for object and value - * properties and elements in the object, and expando properties in unboxed - * objects. + * property types account for object and value properties and elements in + * the object. * * For accesses on these properties, the correspondence is as follows: * @@ -338,10 +298,9 @@ class ObjectGroup : public gc::TenuredCell * 2. Array lengths are special cased by the compiler and VM and are not * reflected in property types. * - * 3. In typed objects (but not unboxed objects), the initial values of - * properties (null pointers and undefined values) are not reflected in - * the property types. These values are always possible when reading the - * property. + * 3. In typed objects, the initial values of properties (null pointers and + * undefined values) are not reflected in the property types. These + * values are always possible when reading the property. * * We establish these by using write barriers on calls to setProperty and * defineProperty which are on native properties, and on any jitcode which @@ -455,12 +414,6 @@ class ObjectGroup : public gc::TenuredCell return &flags_; } - // Get the bit pattern stored in an object's addendum when it has an - // original unboxed group. - static inline int32_t addendumOriginalUnboxedGroupValue() { - return Addendum_OriginalUnboxedGroup << OBJECT_FLAG_ADDENDUM_SHIFT; - } - inline uint32_t basePropertyCount(); private: @@ -505,14 +458,14 @@ class ObjectGroup : public gc::TenuredCell UnknownIndex // Make an array with an unknown element type. }; - // Create an ArrayObject or UnboxedArrayObject with the specified elements - // and a group specialized for the elements. - static JSObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, - NewObjectKind newKind, - NewArrayKind arrayKind = NewArrayKind::Normal); + // Create an ArrayObject with the specified elements and a group specialized + // for the elements. + static ArrayObject* newArrayObject(ExclusiveContext* cx, const Value* vp, size_t length, + NewObjectKind newKind, + NewArrayKind arrayKind = NewArrayKind::Normal); - // Create a PlainObject or UnboxedPlainObject with the specified properties - // and a group specialized for those properties. + // Create a PlainObject with the specified properties and a group specialized + // for those properties. static JSObject* newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_t nproperties, NewObjectKind newKind); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 4b044c8d8..095ef57ae 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1281,17 +1281,7 @@ * Stack: receiver, obj, propval => obj[propval] */ \ macro(JSOP_GETELEM_SUPER, 125, "getelem-super", NULL, 1, 3, 1, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) \ - /* - * Pushes newly created array for a spread call onto the stack. This has - * the same semantics as JSOP_NEWARRAY, but is distinguished to avoid - * using unboxed arrays in spread calls, which would make compiling spread - * calls in baseline more complex. - * Category: Literals - * Type: Array - * Operands: uint32_t length - * Stack: => obj - */ \ - macro(JSOP_SPREADCALLARRAY, 126, "spreadcallarray", NULL, 5, 0, 1, JOF_UINT32) \ + macro(JSOP_UNUSED126, 126, "unused126", NULL, 5, 0, 1, JOF_UINT32) \ \ /* * Defines the given function on the current scope. diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp index 97df908c3..e95e8a208 100644 --- a/js/src/vm/ReceiverGuard.cpp +++ b/js/src/vm/ReceiverGuard.cpp @@ -7,7 +7,6 @@ #include "vm/ReceiverGuard.h" #include "builtin/TypedObject.h" -#include "vm/UnboxedObject.h" #include "jsobjinlines.h" using namespace js; @@ -16,11 +15,7 @@ ReceiverGuard::ReceiverGuard(JSObject* obj) : group(nullptr), shape(nullptr) { if (obj) { - if (obj->is()) { - group = obj->group(); - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) - shape = expando->lastProperty(); - } else if (obj->is() || obj->is()) { + if (obj->is()) { group = obj->group(); } else { shape = obj->maybeShape(); @@ -33,9 +28,7 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape) { if (group) { const Class* clasp = group->clasp(); - if (clasp == &UnboxedPlainObject::class_) { - // Keep both group and shape. - } else if (clasp == &UnboxedArrayObject::class_ || IsTypedObjectClass(clasp)) { + if (IsTypedObjectClass(clasp)) { this->shape = nullptr; } else { this->group = nullptr; @@ -46,12 +39,8 @@ ReceiverGuard::ReceiverGuard(ObjectGroup* group, Shape* shape) /* static */ int32_t HeapReceiverGuard::keyBits(JSObject* obj) { - if (obj->is()) { - // Both the group and shape need to be guarded for unboxed plain objects. - return obj->as().maybeExpando() ? 0 : 1; - } - if (obj->is() || obj->is()) { - // Only the group needs to be guarded for unboxed arrays and typed objects. + if (obj->is()) { + // Only the group needs to be guarded for typed objects. return 2; } // Other objects only need the shape to be guarded. diff --git a/js/src/vm/ReceiverGuard.h b/js/src/vm/ReceiverGuard.h index 459cc0012..c14f0d83b 100644 --- a/js/src/vm/ReceiverGuard.h +++ b/js/src/vm/ReceiverGuard.h @@ -28,11 +28,6 @@ namespace js { // TypedObject: The structure of a typed object is determined by its group. // All typed objects with the same group have the same class, prototype, and // own properties. -// -// UnboxedPlainObject: The structure of an unboxed plain object is determined -// by its group and its expando object's shape, if there is one. All unboxed -// plain objects with the same group and expando shape have the same -// properties except those stored in the expando's dense elements. class HeapReceiverGuard; class RootedReceiverGuard; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 87e95c893..c5f2cf5f3 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -82,7 +82,7 @@ InterpreterFrame::isNonGlobalEvalFrame() const return isEvalFrame() && script()->bodyScope()->as().isNonGlobal(); } -JSObject* +ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) { MOZ_ASSERT(script()->hasRest()); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 552738d89..dc9306c99 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -523,7 +523,7 @@ class InterpreterFrame ArgumentsObject& argsObj() const; void initArgsObj(ArgumentsObject& argsobj); - JSObject* createRestParameter(JSContext* cx); + ArrayObject* createRestParameter(JSContext* cx); /* * Environment chain diff --git a/js/src/vm/TypeInference-inl.h b/js/src/vm/TypeInference-inl.h index da47fa898..2af252cea 100644 --- a/js/src/vm/TypeInference-inl.h +++ b/js/src/vm/TypeInference-inl.h @@ -23,7 +23,6 @@ #include "vm/SharedArrayObject.h" #include "vm/StringObject.h" #include "vm/TypedArrayObject.h" -#include "vm/UnboxedObject.h" #include "jscntxtinlines.h" @@ -285,10 +284,6 @@ TypeIdString(jsid id) */ struct AutoEnterAnalysis { - // For use when initializing an UnboxedLayout. The UniquePtr's destructor - // must run when GC is not suppressed. - UniquePtr unboxedLayoutToCleanUp; - // Prevent GC activity in the middle of analysis. gc::AutoSuppressGC suppressGC; diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 4775a2dea..26ade2948 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -35,7 +35,6 @@ #include "vm/Opcodes.h" #include "vm/Shape.h" #include "vm/Time.h" -#include "vm/UnboxedObject.h" #include "jsatominlines.h" #include "jsscriptinlines.h" @@ -297,9 +296,6 @@ js::ObjectGroupHasProperty(JSContext* cx, ObjectGroup* group, jsid id, const Val return true; } } - JSObject* obj = &value.toObject(); - if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup()) - return true; } if (!types->hasType(type)) { @@ -1944,33 +1940,6 @@ class ConstraintDataFreezeObjectForTypedArrayData } }; -// Constraint which triggers recompilation if an unboxed object in some group -// is converted to a native object. -class ConstraintDataFreezeObjectForUnboxedConvertedToNative -{ - public: - ConstraintDataFreezeObjectForUnboxedConvertedToNative() - {} - - const char* kind() { return "freezeObjectForUnboxedConvertedToNative"; } - - bool invalidateOnNewType(TypeSet::Type type) { return false; } - bool invalidateOnNewPropertyState(TypeSet* property) { return false; } - bool invalidateOnNewObjectState(ObjectGroup* group) { - return group->unboxedLayout().nativeGroup() != nullptr; - } - - bool constraintHolds(JSContext* cx, - const HeapTypeSetKey& property, TemporaryTypeSet* expected) - { - return !invalidateOnNewObjectState(property.object()->maybeGroup()); - } - - bool shouldSweep() { return false; } - - JSCompartment* maybeCompartment() { return nullptr; } -}; - } /* anonymous namespace */ void @@ -1995,17 +1964,6 @@ TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* co ConstraintDataFreezeObjectForTypedArrayData(tarray))); } -void -TypeSet::ObjectKey::watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints) -{ - HeapTypeSetKey objectProperty = property(JSID_EMPTY); - LifoAlloc* alloc = constraints->alloc(); - - typedef CompilerConstraintInstance T; - constraints->add(alloc->new_(alloc, objectProperty, - ConstraintDataFreezeObjectForUnboxedConvertedToNative())); -} - static void ObjectStateChange(ExclusiveContext* cxArg, ObjectGroup* group, bool markingUnknown) { @@ -2516,8 +2474,6 @@ TemporaryTypeSet::propertyNeedsBarrier(CompilerConstraintList* constraints, jsid bool js::ClassCanHaveExtraProperties(const Class* clasp) { - if (clasp == &UnboxedPlainObject::class_ || clasp == &UnboxedArrayObject::class_) - return false; return clasp->getResolve() || clasp->getOpsLookupProperty() || clasp->getOpsGetProperty() @@ -2816,15 +2772,6 @@ js::AddTypePropertyId(ExclusiveContext* cx, ObjectGroup* group, JSObject* obj, j // from acquiring the fully initialized group. if (group->newScript() && group->newScript()->initializedGroup()) AddTypePropertyId(cx, group->newScript()->initializedGroup(), nullptr, id, type); - - // Maintain equivalent type information for unboxed object groups and their - // corresponding native group. Since type sets might contain the unboxed - // group but not the native group, this ensures optimizations based on the - // unboxed group are valid for the native group. - if (group->maybeUnboxedLayout() && group->maybeUnboxedLayout()->nativeGroup()) - AddTypePropertyId(cx, group->maybeUnboxedLayout()->nativeGroup(), nullptr, id, type); - if (ObjectGroup* unboxedGroup = group->maybeOriginalUnboxedGroup()) - AddTypePropertyId(cx, unboxedGroup, nullptr, id, type); } void @@ -2896,12 +2843,6 @@ ObjectGroup::setFlags(ExclusiveContext* cx, ObjectGroupFlags flags) // acquired properties analysis. if (newScript() && newScript()->initializedGroup()) newScript()->initializedGroup()->setFlags(cx, flags); - - // Propagate flag changes between unboxed and corresponding native groups. - if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup()) - maybeUnboxedLayout()->nativeGroup()->setFlags(cx, flags); - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - unboxedGroup->setFlags(cx, flags); } void @@ -2934,13 +2875,6 @@ ObjectGroup::markUnknown(ExclusiveContext* cx) prop->types.setNonDataProperty(cx); } } - - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - MarkObjectGroupUnknownProperties(cx, unboxedGroup); - if (maybeUnboxedLayout() && maybeUnboxedLayout()->nativeGroup()) - MarkObjectGroupUnknownProperties(cx, maybeUnboxedLayout()->nativeGroup()); - if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup()) - MarkObjectGroupUnknownProperties(cx, unboxedGroup); } TypeNewScript* @@ -2948,8 +2882,6 @@ ObjectGroup::anyNewScript() { if (newScript()) return newScript(); - if (maybeUnboxedLayout()) - return unboxedLayout().newScript(); return nullptr; } @@ -2979,10 +2911,7 @@ ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup* replacement) MOZ_ASSERT(!replacement); } - if (this->newScript()) - setAddendum(Addendum_None, nullptr, writeBarrier); - else - unboxedLayout().setNewScript(nullptr, writeBarrier); + setAddendum(Addendum_None, nullptr, writeBarrier); } void @@ -3404,7 +3333,7 @@ JSFunction::setTypeForScriptedFunction(ExclusiveContext* cx, HandleFunction fun, ///////////////////////////////////////////////////////////////////// void -PreliminaryObjectArray::registerNewObject(JSObject* res) +PreliminaryObjectArray::registerNewObject(PlainObject* res) { // The preliminary object pointers are weak, and won't be swept properly // during nursery collections, so the preliminary objects need to be @@ -3422,7 +3351,7 @@ PreliminaryObjectArray::registerNewObject(JSObject* res) } void -PreliminaryObjectArray::unregisterObject(JSObject* obj) +PreliminaryObjectArray::unregisterObject(PlainObject* obj) { for (size_t i = 0; i < COUNT; i++) { if (objects[i] == obj) { @@ -3462,22 +3391,6 @@ PreliminaryObjectArray::sweep() for (size_t i = 0; i < COUNT; i++) { JSObject** ptr = &objects[i]; if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) { - // Before we clear this reference, change the object's group to the - // Object.prototype group. This is done to ensure JSObject::finalize - // sees a NativeObject Class even if we change the current group's - // Class to one of the unboxed object classes in the meantime. If - // the compartment's global is dead, we don't do anything as the - // group's Class is not going to change in that case. - JSObject* obj = *ptr; - GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal(); - if (global && !obj->isSingleton()) { - JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object); - obj->setGroup(objectProto->groupRaw()); - MOZ_ASSERT(obj->is()); - MOZ_ASSERT(obj->getClass() == objectProto->getClass()); - MOZ_ASSERT(!obj->getClass()->hasFinalize()); - } - *ptr = nullptr; } } @@ -3577,17 +3490,11 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro } } - TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects); - if (group->maybeUnboxedLayout()) - return; - - if (shape()) { - // We weren't able to use an unboxed layout, but since the preliminary - // objects still reflect the template object's properties, and all - // objects in the future will be created with those properties, the - // properties can be marked as definite for objects in the group. - group->addDefiniteProperties(cx, shape()); - } + // Since the preliminary objects still reflect the template object's + // properties, and all objects in the future will be created with those + // properties, the properties can be marked as definitive for objects in + // the group. + group->addDefiniteProperties(cx, shape()); } ///////////////////////////////////////////////////////////////////// @@ -3601,7 +3508,6 @@ TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); MOZ_ASSERT(!group->newScript()); - MOZ_ASSERT(!group->maybeUnboxedLayout()); // rollbackPartiallyInitializedObjects expects function_ to be // canonicalized. @@ -3861,34 +3767,9 @@ TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, PodCopy(initializerList, initializerVector.begin(), initializerVector.length()); } - // Try to use an unboxed representation for the group. - if (!TryConvertToUnboxedLayout(cx, enter, templateObject()->lastProperty(), group, preliminaryObjects)) - return false; - js_delete(preliminaryObjects); preliminaryObjects = nullptr; - if (group->maybeUnboxedLayout()) { - // An unboxed layout was constructed for the group, and this has already - // been hooked into it. - MOZ_ASSERT(group->unboxedLayout().newScript() == this); - destroyNewScript.group = nullptr; - - // Clear out the template object, which is not used for TypeNewScripts - // with an unboxed layout. Currently it is a mutant object with a - // non-native group and native shape, so make it safe for GC by changing - // its group to the default for its prototype. - AutoEnterOOMUnsafeRegion oomUnsafe; - ObjectGroup* plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_, - group->proto()); - if (!plainGroup) - oomUnsafe.crash("TypeNewScript::maybeAnalyze"); - templateObject_->setGroup(plainGroup); - templateObject_ = nullptr; - - return true; - } - if (prefixShape->slotSpan() == templateObject()->slotSpan()) { // The definite properties analysis found exactly the properties that // are held in common by the preliminary objects. No further analysis @@ -3984,12 +3865,6 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g continue; } - if (thisv.toObject().is()) { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject())) - oomUnsafe.crash("rollbackPartiallyInitializedObjects"); - } - // Found a matching frame. RootedPlainObject obj(cx, &thisv.toObject().as()); @@ -4183,12 +4058,6 @@ ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom) // Object sets containing objects with unknown properties might // not be complete. Mark the type set as unknown, which it will // be treated as during Ion compilation. - // - // Note that we don't have to do this when the type set might - // be missing the native group corresponding to an unboxed - // object group. In this case, the native group points to the - // unboxed object group via its addendum, so as long as objects - // with either group exist, neither group will be finalized. flags |= TYPE_FLAG_ANYOBJECT; clearObjects(); objectCount = 0; @@ -4272,21 +4141,6 @@ ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom) Maybe fallbackOOM; EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM); - if (maybeUnboxedLayout()) { - // Remove unboxed layouts that are about to be finalized from the - // compartment wide list while we are still on the main thread. - ObjectGroup* group = this; - if (IsAboutToBeFinalizedUnbarriered(&group)) - unboxedLayout().detachFromCompartment(); - - if (unboxedLayout().newScript()) - unboxedLayout().newScript()->sweep(); - - // Discard constructor code to avoid holding onto ExecutablePools. - if (zone()->isGCCompacting()) - unboxedLayout().setConstructorCode(nullptr); - } - if (maybePreliminaryObjects()) maybePreliminaryObjects()->sweep(); diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 0f1cd4936..94ce7e871 100644 --- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -262,7 +262,6 @@ class TypeSet bool hasStableClassAndProto(CompilerConstraintList* constraints); void watchStateChangeForInlinedCall(CompilerConstraintList* constraints); void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints); - void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints); HeapTypeSetKey property(jsid id); void ensureTrackedProperty(JSContext* cx, jsid id); @@ -815,8 +814,8 @@ class PreliminaryObjectArray public: PreliminaryObjectArray() = default; - void registerNewObject(JSObject* res); - void unregisterObject(JSObject* obj); + void registerNewObject(PlainObject* res); + void unregisterObject(PlainObject* obj); JSObject* get(size_t i) const { MOZ_ASSERT(i < COUNT); diff --git a/js/src/vm/UnboxedObject-inl.h b/js/src/vm/UnboxedObject-inl.h deleted file mode 100644 index 93ad7bf28..000000000 --- a/js/src/vm/UnboxedObject-inl.h +++ /dev/null @@ -1,840 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef vm_UnboxedObject_inl_h -#define vm_UnboxedObject_inl_h - -#include "vm/UnboxedObject.h" - -#include "gc/StoreBuffer-inl.h" -#include "vm/ArrayObject-inl.h" -#include "vm/NativeObject-inl.h" - -namespace js { - -static inline Value -GetUnboxedValue(uint8_t* p, JSValueType type, bool maybeUninitialized) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - return BooleanValue(*p != 0); - - case JSVAL_TYPE_INT32: - return Int32Value(*reinterpret_cast(p)); - - case JSVAL_TYPE_DOUBLE: { - // During unboxed plain object creation, non-GC thing properties are - // left uninitialized. This is normally fine, since the properties will - // be filled in shortly, but if they are read before that happens we - // need to make sure that doubles are canonical. - double d = *reinterpret_cast(p); - if (maybeUninitialized) - return DoubleValue(JS::CanonicalizeNaN(d)); - return DoubleValue(d); - } - - case JSVAL_TYPE_STRING: - return StringValue(*reinterpret_cast(p)); - - case JSVAL_TYPE_OBJECT: - return ObjectOrNullValue(*reinterpret_cast(p)); - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -static inline void -SetUnboxedValueNoTypeChange(JSObject* unboxedObject, - uint8_t* p, JSValueType type, const Value& v, - bool preBarrier) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - *p = v.toBoolean(); - return; - - case JSVAL_TYPE_INT32: - *reinterpret_cast(p) = v.toInt32(); - return; - - case JSVAL_TYPE_DOUBLE: - *reinterpret_cast(p) = v.toNumber(); - return; - - case JSVAL_TYPE_STRING: { - MOZ_ASSERT(!IsInsideNursery(v.toString())); - JSString** np = reinterpret_cast(p); - if (preBarrier) - JSString::writeBarrierPre(*np); - *np = v.toString(); - return; - } - - case JSVAL_TYPE_OBJECT: { - JSObject** np = reinterpret_cast(p); - - // Manually trigger post barriers on the whole object. If we treat - // the pointer as a HeapPtrObject we will get confused later if the - // object is converted to its native representation. - JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(obj) && !IsInsideNursery(unboxedObject)) { - JSRuntime* rt = unboxedObject->runtimeFromMainThread(); - rt->gc.storeBuffer.putWholeCell(unboxedObject); - } - - if (preBarrier) - JSObject::writeBarrierPre(*np); - *np = obj; - return; - } - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -static inline bool -SetUnboxedValue(ExclusiveContext* cx, JSObject* unboxedObject, jsid id, - uint8_t* p, JSValueType type, const Value& v, bool preBarrier) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: - if (v.isBoolean()) { - *p = v.toBoolean(); - return true; - } - return false; - - case JSVAL_TYPE_INT32: - if (v.isInt32()) { - *reinterpret_cast(p) = v.toInt32(); - return true; - } - return false; - - case JSVAL_TYPE_DOUBLE: - if (v.isNumber()) { - *reinterpret_cast(p) = v.toNumber(); - return true; - } - return false; - - case JSVAL_TYPE_STRING: - if (v.isString()) { - MOZ_ASSERT(!IsInsideNursery(v.toString())); - JSString** np = reinterpret_cast(p); - if (preBarrier) - JSString::writeBarrierPre(*np); - *np = v.toString(); - return true; - } - return false; - - case JSVAL_TYPE_OBJECT: - if (v.isObjectOrNull()) { - JSObject** np = reinterpret_cast(p); - - // Update property types when writing object properties. Types for - // other properties were captured when the unboxed layout was - // created. - AddTypePropertyId(cx, unboxedObject, id, v); - - // As above, trigger post barriers on the whole object. - JSObject* obj = v.toObjectOrNull(); - if (IsInsideNursery(v.toObjectOrNull()) && !IsInsideNursery(unboxedObject)) { - JSRuntime* rt = unboxedObject->runtimeFromMainThread(); - rt->gc.storeBuffer.putWholeCell(unboxedObject); - } - - if (preBarrier) - JSObject::writeBarrierPre(*np); - *np = obj; - return true; - } - return false; - - default: - MOZ_CRASH("Invalid type for unboxed value"); - } -} - -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -inline const UnboxedLayout& -UnboxedPlainObject::layout() const -{ - return group()->unboxedLayout(); -} - -///////////////////////////////////////////////////////////////////// -// UnboxedArrayObject -///////////////////////////////////////////////////////////////////// - -inline const UnboxedLayout& -UnboxedArrayObject::layout() const -{ - return group()->unboxedLayout(); -} - -inline void -UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length) -{ - if (length > INT32_MAX) { - // Track objects with overflowing lengths in type information. - MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW); - } - - length_ = length; -} - -inline void -UnboxedArrayObject::setInitializedLength(uint32_t initlen) -{ - if (initlen < initializedLength()) { - switch (elementType()) { - case JSVAL_TYPE_STRING: - for (size_t i = initlen; i < initializedLength(); i++) - triggerPreBarrier(i); - break; - case JSVAL_TYPE_OBJECT: - for (size_t i = initlen; i < initializedLength(); i++) - triggerPreBarrier(i); - break; - default: - MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(elementType())); - } - } - setInitializedLengthNoBarrier(initlen); -} - -template -inline bool -UnboxedArrayObject::setElementSpecific(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true); -} - -template -inline void -UnboxedArrayObject::setElementNoTypeChangeSpecific(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ true); -} - -template -inline bool -UnboxedArrayObject::initElementSpecific(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false); -} - -template -inline void -UnboxedArrayObject::initElementNoTypeChangeSpecific(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false); -} - -template -inline Value -UnboxedArrayObject::getElementSpecific(size_t index) -{ - MOZ_ASSERT(index < initializedLength()); - MOZ_ASSERT(Type == elementType()); - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - return GetUnboxedValue(p, Type, /* maybeUninitialized = */ false); -} - -template -inline void -UnboxedArrayObject::triggerPreBarrier(size_t index) -{ - MOZ_ASSERT(UnboxedTypeNeedsPreBarrier(Type)); - - uint8_t* p = elements() + index * UnboxedTypeSize(Type); - - switch (Type) { - case JSVAL_TYPE_STRING: { - JSString** np = reinterpret_cast(p); - JSString::writeBarrierPre(*np); - break; - } - - case JSVAL_TYPE_OBJECT: { - JSObject** np = reinterpret_cast(p); - JSObject::writeBarrierPre(*np); - break; - } - - default: - MOZ_CRASH("Bad type"); - } -} - -///////////////////////////////////////////////////////////////////// -// Combined methods for NativeObject and UnboxedArrayObject accesses. -///////////////////////////////////////////////////////////////////// - -static inline bool -HasAnyBoxedOrUnboxedDenseElements(JSObject* obj) -{ - return obj->isNative() || obj->is(); -} - -static inline size_t -GetAnyBoxedOrUnboxedInitializedLength(JSObject* obj) -{ - if (obj->isNative()) - return obj->as().getDenseInitializedLength(); - if (obj->is()) - return obj->as().initializedLength(); - return 0; -} - -static inline size_t -GetAnyBoxedOrUnboxedCapacity(JSObject* obj) -{ - if (obj->isNative()) - return obj->as().getDenseCapacity(); - if (obj->is()) - return obj->as().capacity(); - return 0; -} - -static inline Value -GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index) -{ - if (obj->isNative()) - return obj->as().getDenseElement(index); - return obj->as().getElement(index); -} - -static inline size_t -GetAnyBoxedOrUnboxedArrayLength(JSObject* obj) -{ - if (obj->is()) - return obj->as().length(); - return obj->as().length(); -} - -static inline void -SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length) -{ - if (obj->is()) { - MOZ_ASSERT(length >= obj->as().length()); - obj->as().setLength(cx, length); - } else { - MOZ_ASSERT(length >= obj->as().length()); - obj->as().setLength(cx, length); - } -} - -static inline bool -SetAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (obj->isNative()) { - obj->as().setDenseElementWithType(cx, index, value); - return true; - } - return obj->as().setElement(cx, index, value); -} - -static inline bool -InitAnyBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (obj->isNative()) { - obj->as().initDenseElementWithType(cx, index, value); - return true; - } - return obj->as().initElement(cx, index, value); -} - -///////////////////////////////////////////////////////////////////// -// Template methods for NativeObject and UnboxedArrayObject accesses. -///////////////////////////////////////////////////////////////////// - -static inline JSValueType -GetBoxedOrUnboxedType(JSObject* obj) -{ - if (obj->isNative()) - return JSVAL_TYPE_MAGIC; - return obj->as().elementType(); -} - -template -static inline bool -HasBoxedOrUnboxedDenseElements(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->isNative(); - return obj->is() && obj->as().elementType() == Type; -} - -template -static inline size_t -GetBoxedOrUnboxedInitializedLength(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as().getDenseInitializedLength(); - return obj->as().initializedLength(); -} - -template -static inline DenseElementResult -SetBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen) -{ - size_t oldInitlen = GetBoxedOrUnboxedInitializedLength(obj); - if (Type == JSVAL_TYPE_MAGIC) { - obj->as().setDenseInitializedLength(initlen); - if (initlen < oldInitlen) - obj->as().shrinkElements(cx, initlen); - } else { - obj->as().setInitializedLength(initlen); - if (initlen < oldInitlen) - obj->as().shrinkElements(cx, initlen); - } - return DenseElementResult::Success; -} - -template -static inline size_t -GetBoxedOrUnboxedCapacity(JSObject* obj) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as().getDenseCapacity(); - return obj->as().capacity(); -} - -template -static inline Value -GetBoxedOrUnboxedDenseElement(JSObject* obj, size_t index) -{ - if (Type == JSVAL_TYPE_MAGIC) - return obj->as().getDenseElement(index); - return obj->as().getElementSpecific(index); -} - -template -static inline void -SetBoxedOrUnboxedDenseElementNoTypeChange(JSObject* obj, size_t index, const Value& value) -{ - if (Type == JSVAL_TYPE_MAGIC) - obj->as().setDenseElement(index, value); - else - obj->as().setElementNoTypeChangeSpecific(index, value); -} - -template -static inline bool -SetBoxedOrUnboxedDenseElement(JSContext* cx, JSObject* obj, size_t index, const Value& value) -{ - if (Type == JSVAL_TYPE_MAGIC) { - obj->as().setDenseElementWithType(cx, index, value); - return true; - } - return obj->as().setElementSpecific(cx, index, value); -} - -template -static inline DenseElementResult -EnsureBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count) -{ - if (Type == JSVAL_TYPE_MAGIC) { - if (!obj->as().ensureElements(cx, count)) - return DenseElementResult::Failure; - } else { - if (obj->as().capacity() < count) { - if (!obj->as().growElements(cx, count)) - return DenseElementResult::Failure; - } - } - return DenseElementResult::Success; -} - -template -static inline DenseElementResult -SetOrExtendBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update) -{ - if (Type == JSVAL_TYPE_MAGIC) { - NativeObject* nobj = &obj->as(); - - if (nobj->denseElementsAreFrozen()) - return DenseElementResult::Incomplete; - - if (obj->is() && - !obj->as().lengthIsWritable() && - start + count >= obj->as().length()) - { - return DenseElementResult::Incomplete; - } - - DenseElementResult result = nobj->ensureDenseElements(cx, start, count); - if (result != DenseElementResult::Success) - return result; - - if (obj->is() && start + count >= obj->as().length()) - obj->as().setLengthInt32(start + count); - - if (updateTypes == ShouldUpdateTypes::DontUpdate && !nobj->shouldConvertDoubleElements()) { - nobj->copyDenseElements(start, vp, count); - } else { - for (size_t i = 0; i < count; i++) - nobj->setDenseElementWithType(cx, start + i, vp[i]); - } - - return DenseElementResult::Success; - } - - UnboxedArrayObject* nobj = &obj->as(); - - if (start > nobj->initializedLength()) - return DenseElementResult::Incomplete; - - if (start + count >= UnboxedArrayObject::MaximumCapacity) - return DenseElementResult::Incomplete; - - if (start + count > nobj->capacity() && !nobj->growElements(cx, start + count)) - return DenseElementResult::Failure; - - size_t oldInitlen = nobj->initializedLength(); - - // Overwrite any existing elements covered by the new range. If we fail - // after this point due to some incompatible type being written to the - // object's elements, afterwards the contents will be different from when - // we started. The caller must retry the operation using a generic path, - // which will overwrite the already-modified elements as well as the ones - // that were left alone. - size_t i = 0; - if (updateTypes == ShouldUpdateTypes::DontUpdate) { - for (size_t j = start; i < count && j < oldInitlen; i++, j++) - nobj->setElementNoTypeChangeSpecific(j, vp[i]); - } else { - for (size_t j = start; i < count && j < oldInitlen; i++, j++) { - if (!nobj->setElementSpecific(cx, j, vp[i])) - return DenseElementResult::Incomplete; - } - } - - if (i != count) { - obj->as().setInitializedLength(start + count); - if (updateTypes == ShouldUpdateTypes::DontUpdate) { - for (; i < count; i++) - nobj->initElementNoTypeChangeSpecific(start + i, vp[i]); - } else { - for (; i < count; i++) { - if (!nobj->initElementSpecific(cx, start + i, vp[i])) { - nobj->setInitializedLengthNoBarrier(oldInitlen); - return DenseElementResult::Incomplete; - } - } - } - } - - if (start + count >= nobj->length()) - nobj->setLength(cx, start + count); - - return DenseElementResult::Success; -} - -template -static inline DenseElementResult -MoveBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, uint32_t dstStart, uint32_t srcStart, - uint32_t length) -{ - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(obj)); - - if (Type == JSVAL_TYPE_MAGIC) { - if (obj->as().denseElementsAreFrozen()) - return DenseElementResult::Incomplete; - - if (!obj->as().maybeCopyElementsForWrite(cx)) - return DenseElementResult::Failure; - obj->as().moveDenseElements(dstStart, srcStart, length); - } else { - uint8_t* data = obj->as().elements(); - size_t elementSize = UnboxedTypeSize(Type); - - if (UnboxedTypeNeedsPreBarrier(Type) && - JS::shadow::Zone::asShadowZone(obj->zone())->needsIncrementalBarrier()) - { - // Trigger pre barriers on any elements we are overwriting. See - // NativeObject::moveDenseElements. No post barrier is needed as - // only whole cell post barriers are used with unboxed objects. - for (size_t i = 0; i < length; i++) - obj->as().triggerPreBarrier(dstStart + i); - } - - memmove(data + dstStart * elementSize, - data + srcStart * elementSize, - length * elementSize); - } - - return DenseElementResult::Success; -} - -template -static inline DenseElementResult -CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(src)); - MOZ_ASSERT(HasBoxedOrUnboxedDenseElements(dst)); - MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(dst) == dstStart); - MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength(src) >= srcStart + length); - MOZ_ASSERT(GetBoxedOrUnboxedCapacity(dst) >= dstStart + length); - - SetBoxedOrUnboxedInitializedLength(cx, dst, dstStart + length); - - if (DstType == JSVAL_TYPE_MAGIC) { - if (SrcType == JSVAL_TYPE_MAGIC) { - const Value* vp = src->as().getDenseElements() + srcStart; - dst->as().initDenseElements(dstStart, vp, length); - } else { - for (size_t i = 0; i < length; i++) { - Value v = GetBoxedOrUnboxedDenseElement(src, srcStart + i); - dst->as().initDenseElement(dstStart + i, v); - } - } - } else if (DstType == SrcType) { - uint8_t* dstData = dst->as().elements(); - uint8_t* srcData = src->as().elements(); - size_t elementSize = UnboxedTypeSize(DstType); - - memcpy(dstData + dstStart * elementSize, - srcData + srcStart * elementSize, - length * elementSize); - - // Add a store buffer entry if we might have copied a nursery pointer to dst. - if (UnboxedTypeNeedsPostBarrier(DstType) && !IsInsideNursery(dst)) - dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCell(dst); - } else if (DstType == JSVAL_TYPE_DOUBLE && SrcType == JSVAL_TYPE_INT32) { - uint8_t* dstData = dst->as().elements(); - uint8_t* srcData = src->as().elements(); - - for (size_t i = 0; i < length; i++) { - int32_t v = *reinterpret_cast(srcData + (srcStart + i) * sizeof(int32_t)); - *reinterpret_cast(dstData + (dstStart + i) * sizeof(double)) = v; - } - } else { - for (size_t i = 0; i < length; i++) { - Value v = GetBoxedOrUnboxedDenseElement(src, srcStart + i); - dst->as().initElementNoTypeChangeSpecific(dstStart + i, v); - } - } - - return DenseElementResult::Success; -} - -///////////////////////////////////////////////////////////////////// -// Dispatch to specialized methods based on the type of an object. -///////////////////////////////////////////////////////////////////// - -// Goop to fix MSVC. See DispatchTraceKindTyped in TraceKind.h. -// The clang-cl front end defines _MSC_VER, but still requires the explicit -// template declaration, so we must test for __clang__ here as well. -#if defined(_MSC_VER) && !defined(__clang__) -# define DEPENDENT_TEMPLATE_HINT -#else -# define DEPENDENT_TEMPLATE_HINT template -#endif - -// Function to dispatch a method specialized to whatever boxed or unboxed dense -// elements which an input object has. -template -DenseElementResult -CallBoxedOrUnboxedSpecialization(F f, JSObject* obj) -{ - if (!HasAnyBoxedOrUnboxedDenseElements(obj)) - return DenseElementResult::Incomplete; - switch (GetBoxedOrUnboxedType(obj)) { - case JSVAL_TYPE_MAGIC: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - case JSVAL_TYPE_BOOLEAN: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - case JSVAL_TYPE_INT32: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - case JSVAL_TYPE_DOUBLE: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - case JSVAL_TYPE_STRING: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - case JSVAL_TYPE_OBJECT: - return f. DEPENDENT_TEMPLATE_HINT operator()(); - default: - MOZ_CRASH(); - } -} - -// As above, except the specialization can reflect the unboxed type of two objects. -template -DenseElementResult -CallBoxedOrUnboxedSpecialization(F f, JSObject* obj1, JSObject* obj2) -{ - if (!HasAnyBoxedOrUnboxedDenseElements(obj1) || !HasAnyBoxedOrUnboxedDenseElements(obj2)) - return DenseElementResult::Incomplete; - -#define SPECIALIZE_OBJ2(TYPE) \ - switch (GetBoxedOrUnboxedType(obj2)) { \ - case JSVAL_TYPE_MAGIC: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - case JSVAL_TYPE_BOOLEAN: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - case JSVAL_TYPE_INT32: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - case JSVAL_TYPE_DOUBLE: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - case JSVAL_TYPE_STRING: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - case JSVAL_TYPE_OBJECT: \ - return f. DEPENDENT_TEMPLATE_HINT operator()(); \ - default: \ - MOZ_CRASH(); \ - } - - switch (GetBoxedOrUnboxedType(obj1)) { - case JSVAL_TYPE_MAGIC: - SPECIALIZE_OBJ2(JSVAL_TYPE_MAGIC) - case JSVAL_TYPE_BOOLEAN: - SPECIALIZE_OBJ2(JSVAL_TYPE_BOOLEAN) - case JSVAL_TYPE_INT32: - SPECIALIZE_OBJ2(JSVAL_TYPE_INT32) - case JSVAL_TYPE_DOUBLE: - SPECIALIZE_OBJ2(JSVAL_TYPE_DOUBLE) - case JSVAL_TYPE_STRING: - SPECIALIZE_OBJ2(JSVAL_TYPE_STRING) - case JSVAL_TYPE_OBJECT: - SPECIALIZE_OBJ2(JSVAL_TYPE_OBJECT) - default: - MOZ_CRASH(); - } - -#undef SPECIALIZE_OBJ2 -} - -#undef DEPENDENT_TEMPLATE_HINT - -#define DefineBoxedOrUnboxedFunctor1(Signature, A) \ -struct Signature ## Functor { \ - A a; \ - explicit Signature ## Functor(A a) \ - : a(a) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C) \ -struct Signature ## Functor { \ - A a; B b; C c; \ - Signature ## Functor(A a, B b, C c) \ - : a(a), b(b), c(c) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor4(Signature, A, B, C, D) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; \ - Signature ## Functor(A a, B b, C c, D d) \ - : a(a), b(b), c(c), d(d) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c, d); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctorPair4(Signature, A, B, C, D) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; \ - Signature ## Functor(A a, B b, C c, D d) \ - : a(a), b(b), c(c), d(d) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c, d); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor5(Signature, A, B, C, D, E) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; \ - Signature ## Functor(A a, B b, C c, D d, E e) \ - : a(a), b(b), c(c), d(d), e(e) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c, d, e); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctor6(Signature, A, B, C, D, E, F) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; F f; \ - Signature ## Functor(A a, B b, C c, D d, E e, F f) \ - : a(a), b(b), c(c), d(d), e(e), f(f) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c, d, e, f); \ - } \ -} - -#define DefineBoxedOrUnboxedFunctorPair6(Signature, A, B, C, D, E, F) \ -struct Signature ## Functor { \ - A a; B b; C c; D d; E e; F f; \ - Signature ## Functor(A a, B b, C c, D d, E e, F f) \ - : a(a), b(b), c(c), d(d), e(e), f(f) \ - {} \ - template \ - DenseElementResult operator()() { \ - return Signature(a, b, c, d, e, f); \ - } \ -} - -DenseElementResult -SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update); - -DenseElementResult -MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, - uint32_t dstStart, uint32_t srcStart, uint32_t length); - -DenseElementResult -CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length); - -void -SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen); - -DenseElementResult -EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t count); - -} // namespace js - -#endif // vm_UnboxedObject_inl_h diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp deleted file mode 100644 index 3018ace67..000000000 --- a/js/src/vm/UnboxedObject.cpp +++ /dev/null @@ -1,2152 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "vm/UnboxedObject-inl.h" - -#include "jit/BaselineIC.h" -#include "jit/ExecutableAllocator.h" -#include "jit/JitCommon.h" -#include "jit/Linker.h" - -#include "jsobjinlines.h" - -#include "gc/Nursery-inl.h" -#include "jit/MacroAssembler-inl.h" -#include "vm/Shape-inl.h" - -using mozilla::ArrayLength; -using mozilla::DebugOnly; -using mozilla::PodCopy; - -using namespace js; - -///////////////////////////////////////////////////////////////////// -// UnboxedLayout -///////////////////////////////////////////////////////////////////// - -void -UnboxedLayout::trace(JSTracer* trc) -{ - for (size_t i = 0; i < properties_.length(); i++) - TraceManuallyBarrieredEdge(trc, &properties_[i].name, "unboxed_layout_name"); - - if (newScript()) - newScript()->trace(trc); - - TraceNullableEdge(trc, &nativeGroup_, "unboxed_layout_nativeGroup"); - TraceNullableEdge(trc, &nativeShape_, "unboxed_layout_nativeShape"); - TraceNullableEdge(trc, &allocationScript_, "unboxed_layout_allocationScript"); - TraceNullableEdge(trc, &replacementGroup_, "unboxed_layout_replacementGroup"); - TraceNullableEdge(trc, &constructorCode_, "unboxed_layout_constructorCode"); -} - -size_t -UnboxedLayout::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) -{ - return mallocSizeOf(this) - + properties_.sizeOfExcludingThis(mallocSizeOf) - + (newScript() ? newScript()->sizeOfIncludingThis(mallocSizeOf) : 0) - + mallocSizeOf(traceList()); -} - -void -UnboxedLayout::setNewScript(TypeNewScript* newScript, bool writeBarrier /* = true */) -{ - if (newScript_ && writeBarrier) - TypeNewScript::writeBarrierPre(newScript_); - newScript_ = newScript; -} - -// Constructor code returns a 0x1 value to indicate the constructor code should -// be cleared. -static const uintptr_t CLEAR_CONSTRUCTOR_CODE_TOKEN = 0x1; - -/* static */ bool -UnboxedLayout::makeConstructorCode(JSContext* cx, HandleObjectGroup group) -{ - gc::AutoSuppressGC suppress(cx); - - using namespace jit; - - if (!cx->compartment()->ensureJitCompartmentExists(cx)) - return false; - - UnboxedLayout& layout = group->unboxedLayout(); - MOZ_ASSERT(!layout.constructorCode()); - - UnboxedPlainObject* templateObject = UnboxedPlainObject::create(cx, group, TenuredObject); - if (!templateObject) - return false; - - JitContext jitContext(cx, nullptr); - - MacroAssembler masm; - - Register propertiesReg, newKindReg; -#ifdef JS_CODEGEN_X86 - propertiesReg = eax; - newKindReg = ecx; - masm.loadPtr(Address(masm.getStackPointer(), sizeof(void*)), propertiesReg); - masm.loadPtr(Address(masm.getStackPointer(), 2 * sizeof(void*)), newKindReg); -#else - propertiesReg = IntArgReg0; - newKindReg = IntArgReg1; -#endif - -#ifdef JS_CODEGEN_ARM64 - // ARM64 communicates stack address via sp, but uses a pseudo-sp for addressing. - masm.initStackPtr(); -#endif - - MOZ_ASSERT(propertiesReg.volatile_()); - MOZ_ASSERT(newKindReg.volatile_()); - - AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); - regs.take(propertiesReg); - regs.take(newKindReg); - Register object = regs.takeAny(), scratch1 = regs.takeAny(), scratch2 = regs.takeAny(); - - LiveGeneralRegisterSet savedNonVolatileRegisters = SavedNonVolatileRegisters(regs); - masm.PushRegsInMask(savedNonVolatileRegisters); - - // The scratch double register might be used by MacroAssembler methods. - if (ScratchDoubleReg.volatile_()) - masm.push(ScratchDoubleReg); - - Label failure, tenuredObject, allocated; - masm.branch32(Assembler::NotEqual, newKindReg, Imm32(GenericObject), &tenuredObject); - masm.branchTest32(Assembler::NonZero, AbsoluteAddress(group->addressOfFlags()), - Imm32(OBJECT_FLAG_PRE_TENURE), &tenuredObject); - - // Allocate an object in the nursery - masm.createGCObject(object, scratch1, templateObject, gc::DefaultHeap, &failure, - /* initFixedSlots = */ false); - - masm.jump(&allocated); - masm.bind(&tenuredObject); - - // Allocate an object in the tenured heap. - masm.createGCObject(object, scratch1, templateObject, gc::TenuredHeap, &failure, - /* initFixedSlots = */ false); - - // If any of the properties being stored are in the nursery, add a store - // buffer entry for the new object. - Label postBarrier; - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - if (property.type == JSVAL_TYPE_OBJECT) { - Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); - Label notObject; - masm.branchTestObject(Assembler::NotEqual, valueAddress, ¬Object); - Register valueObject = masm.extractObject(valueAddress, scratch1); - masm.branchPtrInNurseryChunk(Assembler::Equal, valueObject, scratch2, &postBarrier); - masm.bind(¬Object); - } - } - - masm.jump(&allocated); - masm.bind(&postBarrier); - - LiveGeneralRegisterSet liveVolatileRegisters; - liveVolatileRegisters.add(propertiesReg); - if (object.volatile_()) - liveVolatileRegisters.add(object); - masm.PushRegsInMask(liveVolatileRegisters); - - masm.mov(ImmPtr(cx->runtime()), scratch1); - masm.setupUnalignedABICall(scratch2); - masm.passABIArg(scratch1); - masm.passABIArg(object); - masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, PostWriteBarrier)); - - masm.PopRegsInMask(liveVolatileRegisters); - - masm.bind(&allocated); - - ValueOperand valueOperand; -#ifdef JS_NUNBOX32 - valueOperand = ValueOperand(scratch1, scratch2); -#else - valueOperand = ValueOperand(scratch1); -#endif - - Label failureStoreOther, failureStoreObject; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - Address valueAddress(propertiesReg, i * sizeof(IdValuePair) + offsetof(IdValuePair, value)); - Address targetAddress(object, UnboxedPlainObject::offsetOfData() + property.offset); - - masm.loadValue(valueAddress, valueOperand); - - if (property.type == JSVAL_TYPE_OBJECT) { - HeapTypeSet* types = group->maybeGetProperty(IdToTypeId(NameToId(property.name))); - - Label notObject; - masm.branchTestObject(Assembler::NotEqual, valueOperand, - types->mightBeMIRType(MIRType::Null) ? ¬Object : &failureStoreObject); - - Register payloadReg = masm.extractObject(valueOperand, scratch1); - - if (!types->hasType(TypeSet::AnyObjectType())) { - Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1; - masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject); - } - - masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, - TypedOrValueRegister(MIRType::Object, - AnyRegister(payloadReg)), nullptr); - - if (notObject.used()) { - Label done; - masm.jump(&done); - masm.bind(¬Object); - masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther); - masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT, NullValue(), nullptr); - masm.bind(&done); - } - } else { - masm.storeUnboxedProperty(targetAddress, property.type, - ConstantOrRegister(valueOperand), &failureStoreOther); - } - } - - Label done; - masm.bind(&done); - - if (object != ReturnReg) - masm.movePtr(object, ReturnReg); - - // Restore non-volatile registers which were saved on entry. - if (ScratchDoubleReg.volatile_()) - masm.pop(ScratchDoubleReg); - masm.PopRegsInMask(savedNonVolatileRegisters); - - masm.abiret(); - - masm.bind(&failureStoreOther); - - // There was a failure while storing a value which cannot be stored at all - // in the unboxed object. Initialize the object so it is safe for GC and - // return null. - masm.initUnboxedObjectContents(object, templateObject); - - masm.bind(&failure); - - masm.movePtr(ImmWord(0), object); - masm.jump(&done); - - masm.bind(&failureStoreObject); - - // There was a failure while storing a value to an object slot of the - // unboxed object. If the value is storable, the failure occurred due to - // incomplete type information in the object, so return a token to trigger - // regeneration of the jitcode after a new object is created in the VM. - { - Label isObject; - masm.branchTestObject(Assembler::Equal, valueOperand, &isObject); - masm.branchTestNull(Assembler::NotEqual, valueOperand, &failureStoreOther); - masm.bind(&isObject); - } - - // Initialize the object so it is safe for GC. - masm.initUnboxedObjectContents(object, templateObject); - - masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object); - masm.jump(&done); - - Linker linker(masm); - AutoFlushICache afc("UnboxedObject"); - JitCode* code = linker.newCode(cx, OTHER_CODE); - if (!code) - return false; - - layout.setConstructorCode(code); - return true; -} - -void -UnboxedLayout::detachFromCompartment() -{ - if (isInList()) - remove(); -} - -///////////////////////////////////////////////////////////////////// -// UnboxedPlainObject -///////////////////////////////////////////////////////////////////// - -bool -UnboxedPlainObject::setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, - const Value& v) -{ - uint8_t* p = &data_[property.offset]; - return SetUnboxedValue(cx, this, NameToId(property.name), p, property.type, v, - /* preBarrier = */ true); -} - -Value -UnboxedPlainObject::getValue(const UnboxedLayout::Property& property, - bool maybeUninitialized /* = false */) -{ - uint8_t* p = &data_[property.offset]; - return GetUnboxedValue(p, property.type, maybeUninitialized); -} - -void -UnboxedPlainObject::trace(JSTracer* trc, JSObject* obj) -{ - if (obj->as().expando_) { - TraceManuallyBarrieredEdge(trc, - reinterpret_cast(&obj->as().expando_), - "unboxed_expando"); - } - - const UnboxedLayout& layout = obj->as().layoutDontCheckGeneration(); - const int32_t* list = layout.traceList(); - if (!list) - return; - - uint8_t* data = obj->as().data(); - while (*list != -1) { - GCPtrString* heap = reinterpret_cast(data + *list); - TraceEdge(trc, heap, "unboxed_string"); - list++; - } - list++; - while (*list != -1) { - GCPtrObject* heap = reinterpret_cast(data + *list); - TraceNullableEdge(trc, heap, "unboxed_object"); - list++; - } - - // Unboxed objects don't have Values to trace. - MOZ_ASSERT(*(list + 1) == -1); -} - -/* static */ UnboxedExpandoObject* -UnboxedPlainObject::ensureExpando(JSContext* cx, Handle obj) -{ - if (obj->expando_) - return obj->expando_; - - UnboxedExpandoObject* expando = - NewObjectWithGivenProto(cx, nullptr, gc::AllocKind::OBJECT4); - if (!expando) - return nullptr; - - // Don't track property types for expando objects. This allows Baseline - // and Ion AddSlot ICs to guard on the unboxed group without guarding on - // the expando group. - MarkObjectGroupUnknownProperties(cx, expando->group()); - - // If the expando is tenured then the original object must also be tenured. - // Otherwise barriers triggered on the original object for writes to the - // expando (as can happen in the JIT) won't see the tenured->nursery edge. - // See WholeCellEdges::mark. - MOZ_ASSERT_IF(!IsInsideNursery(expando), !IsInsideNursery(obj)); - - // As with setValue(), we need to manually trigger post barriers on the - // whole object. If we treat the field as a GCPtrObject and later - // convert the object to its native representation, we will end up with a - // corrupted store buffer entry. - if (IsInsideNursery(expando) && !IsInsideNursery(obj)) - cx->runtime()->gc.storeBuffer.putWholeCell(obj); - - obj->expando_ = expando; - return expando; -} - -bool -UnboxedPlainObject::containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const -{ - if (layout().lookup(id)) - return true; - - if (maybeExpando() && maybeExpando()->containsShapeOrElement(cx, id)) - return true; - - return false; -} - -static bool -PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGroup* newGroup) -{ - HeapTypeSet* typeProperty = oldGroup->maybeGetProperty(id); - TypeSet::TypeList types; - if (!typeProperty->enumerateTypes(&types)) { - ReportOutOfMemory(cx); - return false; - } - for (size_t j = 0; j < types.length(); j++) - AddTypePropertyId(cx, newGroup, nullptr, id, types[j]); - return true; -} - -static PlainObject* -MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout) -{ - PlainObject* obj = NewObjectWithGroup(cx, group, layout.getAllocKind(), - TenuredObject); - if (!obj) - return nullptr; - - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE)) - return nullptr; - MOZ_ASSERT(obj->slotSpan() == i + 1); - MOZ_ASSERT(!obj->inDictionaryMode()); - } - - return obj; -} - -/* static */ bool -UnboxedLayout::makeNativeGroup(JSContext* cx, ObjectGroup* group) -{ - AutoEnterAnalysis enter(cx); - - UnboxedLayout& layout = group->unboxedLayout(); - Rooted proto(cx, group->proto()); - - MOZ_ASSERT(!layout.nativeGroup()); - - RootedObjectGroup replacementGroup(cx); - - const Class* clasp = layout.isArray() ? &ArrayObject::class_ : &PlainObject::class_; - - // Immediately clear any new script on the group. This is done by replacing - // the existing new script with one for a replacement default new group. - // This is done so that the size of the replacment group's objects is the - // same as that for the unboxed group, so that we do not see polymorphic - // slot accesses later on for sites that see converted objects from this - // group and objects that were allocated using the replacement new group. - if (layout.newScript()) { - MOZ_ASSERT(!layout.isArray()); - - replacementGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto); - if (!replacementGroup) - return false; - - PlainObject* templateObject = MakeReplacementTemplateObject(cx, replacementGroup, layout); - if (!templateObject) - return false; - - TypeNewScript* replacementNewScript = - TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject); - if (!replacementNewScript) - return false; - - replacementGroup->setNewScript(replacementNewScript); - gc::TraceTypeNewScript(replacementGroup); - - group->clearNewScript(cx, replacementGroup); - } - - // Similarly, if this group is keyed to an allocation site, replace its - // entry with a new group that has no unboxed layout. - if (layout.allocationScript()) { - RootedScript script(cx, layout.allocationScript()); - jsbytecode* pc = layout.allocationPc(); - - replacementGroup = ObjectGroupCompartment::makeGroup(cx, clasp, proto); - if (!replacementGroup) - return false; - - PlainObject* templateObject = &script->getObject(pc)->as(); - replacementGroup->addDefiniteProperties(cx, templateObject->lastProperty()); - - JSProtoKey key = layout.isArray() ? JSProto_Array : JSProto_Object; - cx->compartment()->objectGroups.replaceAllocationSiteGroup(script, pc, key, - replacementGroup); - - // Clear any baseline information at this opcode which might use the old group. - if (script->hasBaselineScript()) { - jit::ICEntry& entry = script->baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc)); - jit::ICFallbackStub* fallback = entry.fallbackStub(); - for (jit::ICStubIterator iter = fallback->beginChain(); !iter.atEnd(); iter++) - iter.unlink(cx); - if (fallback->isNewObject_Fallback()) - fallback->toNewObject_Fallback()->setTemplateObject(nullptr); - else if (fallback->isNewArray_Fallback()) - fallback->toNewArray_Fallback()->setTemplateGroup(replacementGroup); - } - } - - size_t nfixed = layout.isArray() ? 0 : gc::GetGCKindSlots(layout.getAllocKind()); - - if (layout.isArray()) { - // The length shape to use for arrays is cached via a modified initial - // shape for array objects. Create an array now to make sure this entry - // is instantiated. - if (!NewDenseEmptyArray(cx)) - return false; - } - - RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, proto, nfixed, 0)); - if (!shape) - return false; - - MOZ_ASSERT_IF(layout.isArray(), !shape->isEmptyShape() && shape->slotSpan() == 0); - - // Add shapes for each property, if this is for a plain object. - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - - Rooted child(cx, StackShape(shape->base()->unowned(), NameToId(property.name), - i, JSPROP_ENUMERATE, 0)); - shape = cx->zone()->propertyTree.getChild(cx, shape, child); - if (!shape) - return false; - } - - ObjectGroup* nativeGroup = - ObjectGroupCompartment::makeGroup(cx, clasp, proto, - group->flags() & OBJECT_FLAG_DYNAMIC_MASK); - if (!nativeGroup) - return false; - - // No sense propagating if we don't know what we started with. - if (!group->unknownProperties()) { - // Propagate all property types from the old group to the new group. - if (layout.isArray()) { - if (!PropagatePropertyTypes(cx, JSID_VOID, group, nativeGroup)) - return false; - } else { - for (size_t i = 0; i < layout.properties().length(); i++) { - const UnboxedLayout::Property& property = layout.properties()[i]; - jsid id = NameToId(property.name); - if (!PropagatePropertyTypes(cx, id, group, nativeGroup)) - return false; - - // If we are OOM we may not be able to propagate properties. - if (nativeGroup->unknownProperties()) - break; - - HeapTypeSet* nativeProperty = nativeGroup->maybeGetProperty(id); - if (nativeProperty && nativeProperty->canSetDefinite(i)) - nativeProperty->setDefinite(i); - } - } - } else { - // If we skip, though, the new group had better agree. - MOZ_ASSERT(nativeGroup->unknownProperties()); - } - - layout.nativeGroup_ = nativeGroup; - layout.nativeShape_ = shape; - layout.replacementGroup_ = replacementGroup; - - nativeGroup->setOriginalUnboxedGroup(group); - - group->markStateChange(cx); - - return true; -} - -/* static */ bool -UnboxedPlainObject::convertToNative(JSContext* cx, JSObject* obj) -{ - const UnboxedLayout& layout = obj->as().layout(); - UnboxedExpandoObject* expando = obj->as().maybeExpando(); - - if (!layout.nativeGroup()) { - if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) - return false; - - // makeNativeGroup can reentrantly invoke this method. - if (obj->is()) - return true; - } - - AutoValueVector values(cx); - for (size_t i = 0; i < layout.properties().length(); i++) { - // We might be reading properties off the object which have not been - // initialized yet. Make sure any double values we read here are - // canonicalized. - if (!values.append(obj->as().getValue(layout.properties()[i], true))) - return false; - } - - // We are eliminating the expando edge with the conversion, so trigger a - // pre barrier. - JSObject::writeBarrierPre(expando); - - // Additionally trigger a post barrier on the expando itself. Whole cell - // store buffer entries can be added on the original unboxed object for - // writes to the expando (see WholeCellEdges::trace), so after conversion - // we need to make sure the expando itself will still be traced. - if (expando && !IsInsideNursery(expando)) - cx->runtime()->gc.storeBuffer.putWholeCell(expando); - - obj->setGroup(layout.nativeGroup()); - obj->as().setLastPropertyMakeNative(cx, layout.nativeShape()); - - for (size_t i = 0; i < values.length(); i++) - obj->as().initSlotUnchecked(i, values[i]); - - if (expando) { - // Add properties from the expando object to the object, in order. - // Suppress GC here, so that callers don't need to worry about this - // method collecting. The stuff below can only fail due to OOM, in - // which case the object will not have been completely filled back in. - gc::AutoSuppressGC suppress(cx); - - Vector ids(cx); - for (Shape::Range r(expando->lastProperty()); !r.empty(); r.popFront()) { - if (!ids.append(r.front().propid())) - return false; - } - for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) { - if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { - if (!ids.append(INT_TO_JSID(i))) - return false; - } - } - ::Reverse(ids.begin(), ids.end()); - - RootedPlainObject nobj(cx, &obj->as()); - Rooted nexpando(cx, expando); - RootedId id(cx); - Rooted desc(cx); - for (size_t i = 0; i < ids.length(); i++) { - id = ids[i]; - if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc)) - return false; - ObjectOpResult result; - if (!DefineProperty(cx, nobj, id, desc, result)) - return false; - MOZ_ASSERT(result.ok()); - } - } - - return true; -} - -/* static */ -UnboxedPlainObject* -UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind) -{ - AutoSetNewObjectMetadata metadata(cx); - - MOZ_ASSERT(group->clasp() == &class_); - gc::AllocKind allocKind = group->unboxedLayout().getAllocKind(); - - UnboxedPlainObject* res = - NewObjectWithGroup(cx, group, allocKind, newKind); - if (!res) - return nullptr; - - // Overwrite the dummy shape which was written to the object's expando field. - res->initExpando(); - - // Initialize reference fields of the object. All fields in the object will - // be overwritten shortly, but references need to be safe for the GC. - const int32_t* list = res->layout().traceList(); - if (list) { - uint8_t* data = res->data(); - while (*list != -1) { - GCPtrString* heap = reinterpret_cast(data + *list); - heap->init(cx->names().empty); - list++; - } - list++; - while (*list != -1) { - GCPtrObject* heap = reinterpret_cast(data + *list); - heap->init(nullptr); - list++; - } - // Unboxed objects don't have Values to initialize. - MOZ_ASSERT(*(list + 1) == -1); - } - - return res; -} - -/* static */ JSObject* -UnboxedPlainObject::createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind, IdValuePair* properties) -{ - MOZ_ASSERT(newKind == GenericObject || newKind == TenuredObject); - - UnboxedLayout& layout = group->unboxedLayout(); - - if (layout.constructorCode()) { - MOZ_ASSERT(cx->isJSContext()); - - typedef JSObject* (*ConstructorCodeSignature)(IdValuePair*, NewObjectKind); - ConstructorCodeSignature function = - reinterpret_cast(layout.constructorCode()->raw()); - - JSObject* obj; - { - JS::AutoSuppressGCAnalysis nogc; - obj = reinterpret_cast(CALL_GENERATED_2(function, properties, newKind)); - } - if (obj > reinterpret_cast(CLEAR_CONSTRUCTOR_CODE_TOKEN)) - return obj; - - if (obj == reinterpret_cast(CLEAR_CONSTRUCTOR_CODE_TOKEN)) - layout.setConstructorCode(nullptr); - } - - UnboxedPlainObject* obj = UnboxedPlainObject::create(cx, group, newKind); - if (!obj) - return nullptr; - - for (size_t i = 0; i < layout.properties().length(); i++) { - if (!obj->setValue(cx, layout.properties()[i], properties[i].value)) - return NewPlainObjectWithProperties(cx, properties, layout.properties().length(), newKind); - } - -#ifndef JS_CODEGEN_NONE - if (cx->isJSContext() && - !group->unknownProperties() && - !layout.constructorCode() && - cx->asJSContext()->runtime()->jitSupportsFloatingPoint && - jit::CanLikelyAllocateMoreExecutableMemory()) - { - if (!UnboxedLayout::makeConstructorCode(cx->asJSContext(), group)) - return nullptr; - } -#endif - - return obj; -} - -/* static */ bool -UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp) -{ - if (obj->as().containsUnboxedOrExpandoProperty(cx, id)) { - MarkNonNativePropertyFound(propp); - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return LookupProperty(cx, proto, id, objp, propp); -} - -/* static */ bool -UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - if (!desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { - // This define is equivalent to setting an existing property. - if (obj->as().setValue(cx, *property, desc.value())) - return result.succeed(); - } - - // Trying to incompatibly redefine an existing property requires the - // object to be converted to a native object. - if (!convertToNative(cx, obj)) - return false; - - return DefineProperty(cx, obj, id, desc, result); - } - - // Define the property on the expando object. - Rooted expando(cx, ensureExpando(cx, obj.as())); - if (!expando) - return false; - - // Update property types on the unboxed object as well. - AddTypePropertyId(cx, obj, id, desc.value()); - - return DefineProperty(cx, expando, id, desc, result); -} - -/* static */ bool -UnboxedPlainObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) -{ - if (obj->as().containsUnboxedOrExpandoProperty(cx, id)) { - *foundp = true; - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - *foundp = false; - return true; - } - - return HasProperty(cx, proto, id, foundp); -} - -/* static */ bool -UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - vp.set(obj->as().getValue(*property)); - return true; - } - - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - RootedObject nexpando(cx, expando); - return GetProperty(cx, nexpando, receiver, id, vp); - } - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - vp.setUndefined(); - return true; - } - - return GetProperty(cx, proto, receiver, id, vp); -} - -/* static */ bool -UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - if (receiver.isObject() && obj == &receiver.toObject()) { - if (obj->as().setValue(cx, *property, v)) - return result.succeed(); - - if (!convertToNative(cx, obj)) - return false; - return SetProperty(cx, obj, id, v, receiver, result); - } - - return SetPropertyByDefining(cx, id, v, receiver, result); - } - - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - // Update property types on the unboxed object as well. - AddTypePropertyId(cx, obj, id, v); - - RootedObject nexpando(cx, expando); - return SetProperty(cx, nexpando, id, v, receiver, result); - } - } - - return SetPropertyOnProto(cx, obj, id, v, receiver, result); -} - -/* static */ bool -UnboxedPlainObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (const UnboxedLayout::Property* property = layout.lookup(id)) { - desc.value().set(obj->as().getValue(*property)); - desc.setAttributes(JSPROP_ENUMERATE); - desc.object().set(obj); - return true; - } - - if (UnboxedExpandoObject* expando = obj->as().maybeExpando()) { - if (expando->containsShapeOrElement(cx, id)) { - RootedObject nexpando(cx, expando); - if (!GetOwnPropertyDescriptor(cx, nexpando, id, desc)) - return false; - if (desc.object() == nexpando) - desc.object().set(obj); - return true; - } - } - - desc.object().set(nullptr); - return true; -} - -/* static */ bool -UnboxedPlainObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result) -{ - if (!convertToNative(cx, obj)) - return false; - return DeleteProperty(cx, obj, id, result); -} - -/* static */ bool -UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (!convertToNative(cx, obj)) - return false; - return WatchProperty(cx, obj, id, callable); -} - -/* static */ bool -UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly) -{ - // Ignore expando properties here, they are special-cased by the property - // enumeration code. - - const UnboxedLayout::PropertyVector& unboxed = obj->as().layout().properties(); - for (size_t i = 0; i < unboxed.length(); i++) { - if (!properties.append(NameToId(unboxed[i].name))) - return false; - } - - return true; -} - -const Class UnboxedExpandoObject::class_ = { - "UnboxedExpandoObject", - 0 -}; - -static const ClassOps UnboxedPlainObjectClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - nullptr, /* finalize */ - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - UnboxedPlainObject::trace, -}; - -static const ObjectOps UnboxedPlainObjectObjectOps = { - UnboxedPlainObject::obj_lookupProperty, - UnboxedPlainObject::obj_defineProperty, - UnboxedPlainObject::obj_hasProperty, - UnboxedPlainObject::obj_getProperty, - UnboxedPlainObject::obj_setProperty, - UnboxedPlainObject::obj_getOwnPropertyDescriptor, - UnboxedPlainObject::obj_deleteProperty, - UnboxedPlainObject::obj_watch, - nullptr, /* No unwatch needed, as watch() converts the object to native */ - nullptr, /* getElements */ - UnboxedPlainObject::obj_enumerate, - nullptr /* funToString */ -}; - -const Class UnboxedPlainObject::class_ = { - js_Object_str, - Class::NON_NATIVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | - JSCLASS_DELAY_METADATA_BUILDER, - &UnboxedPlainObjectClassOps, - JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - &UnboxedPlainObjectObjectOps -}; - -///////////////////////////////////////////////////////////////////// -// UnboxedArrayObject -///////////////////////////////////////////////////////////////////// - -template -DenseElementResult -AppendUnboxedDenseElements(UnboxedArrayObject* obj, uint32_t initlen, - MutableHandle> values) -{ - for (size_t i = 0; i < initlen; i++) - values.infallibleAppend(obj->getElementSpecific(i)); - return DenseElementResult::Success; -} - -DefineBoxedOrUnboxedFunctor3(AppendUnboxedDenseElements, - UnboxedArrayObject*, uint32_t, MutableHandle>); - -/* static */ bool -UnboxedArrayObject::convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj, - ObjectGroup* group, Shape* shape) -{ - size_t length = obj->as().length(); - size_t initlen = obj->as().initializedLength(); - - Rooted> values(cx, GCVector(cx)); - if (!values.reserve(initlen)) - return false; - - AppendUnboxedDenseElementsFunctor functor(&obj->as(), initlen, &values); - DebugOnly result = CallBoxedOrUnboxedSpecialization(functor, obj); - MOZ_ASSERT(result.value == DenseElementResult::Success); - - obj->setGroup(group); - - ArrayObject* aobj = &obj->as(); - aobj->setLastPropertyMakeNative(cx, shape); - - // Make sure there is at least one element, so that this array does not - // use emptyObjectElements / emptyObjectElementsShared. - if (!aobj->ensureElements(cx, Max(initlen, 1))) - return false; - - MOZ_ASSERT(!aobj->getDenseInitializedLength()); - aobj->setDenseInitializedLength(initlen); - aobj->initDenseElements(0, values.begin(), initlen); - aobj->setLengthInt32(length); - - return true; -} - -/* static */ bool -UnboxedArrayObject::convertToNative(JSContext* cx, JSObject* obj) -{ - const UnboxedLayout& layout = obj->as().layout(); - - if (!layout.nativeGroup()) { - if (!UnboxedLayout::makeNativeGroup(cx, obj->group())) - return false; - } - - return convertToNativeWithGroup(cx, obj, layout.nativeGroup(), layout.nativeShape()); -} - -bool -UnboxedArrayObject::convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group) -{ - MOZ_ASSERT(elementType() == JSVAL_TYPE_INT32); - MOZ_ASSERT(group->unboxedLayout().elementType() == JSVAL_TYPE_DOUBLE); - - Vector values(cx); - if (!values.reserve(initializedLength())) - return false; - for (size_t i = 0; i < initializedLength(); i++) - values.infallibleAppend(getElementSpecific(i).toInt32()); - - uint8_t* newElements; - if (hasInlineElements()) { - newElements = AllocateObjectBuffer(cx, this, capacity() * sizeof(double)); - } else { - newElements = ReallocateObjectBuffer(cx, this, elements(), - capacity() * sizeof(int32_t), - capacity() * sizeof(double)); - } - if (!newElements) - return false; - - setGroup(group); - elements_ = newElements; - - for (size_t i = 0; i < initializedLength(); i++) - setElementNoTypeChangeSpecific(i, DoubleValue(values[i])); - - return true; -} - -/* static */ UnboxedArrayObject* -UnboxedArrayObject::create(ExclusiveContext* cx, HandleObjectGroup group, uint32_t length, - NewObjectKind newKind, uint32_t maxLength) -{ - MOZ_ASSERT(length <= MaximumCapacity); - - MOZ_ASSERT(group->clasp() == &class_); - uint32_t elementSize = UnboxedTypeSize(group->unboxedLayout().elementType()); - uint32_t capacity = Min(length, maxLength); - uint32_t nbytes = offsetOfInlineElements() + elementSize * capacity; - - UnboxedArrayObject* res; - if (nbytes <= JSObject::MAX_BYTE_SIZE) { - gc::AllocKind allocKind = gc::GetGCObjectKindForBytes(nbytes); - - // If there was no provided length information, pick an allocation kind - // to accommodate small arrays (as is done for normal native arrays). - if (capacity == 0) - allocKind = gc::AllocKind::OBJECT8; - - res = NewObjectWithGroup(cx, group, allocKind, newKind); - if (!res) - return nullptr; - res->setInitializedLengthNoBarrier(0); - res->setInlineElements(); - - size_t actualCapacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize; - MOZ_ASSERT(actualCapacity >= capacity); - res->setCapacityIndex(exactCapacityIndex(actualCapacity)); - } else { - res = NewObjectWithGroup(cx, group, gc::AllocKind::OBJECT0, newKind); - if (!res) - return nullptr; - res->setInitializedLengthNoBarrier(0); - - uint32_t capacityIndex = (capacity == length) - ? CapacityMatchesLengthIndex - : chooseCapacityIndex(capacity, length); - uint32_t actualCapacity = computeCapacity(capacityIndex, length); - - res->elements_ = AllocateObjectBuffer(cx, res, actualCapacity * elementSize); - if (!res->elements_) { - // Make the object safe for GC. - res->setInlineElements(); - return nullptr; - } - - res->setCapacityIndex(capacityIndex); - } - - res->setLength(cx, length); - return res; -} - -bool -UnboxedArrayObject::setElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ true); -} - -bool -UnboxedArrayObject::initElement(ExclusiveContext* cx, size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return SetUnboxedValue(cx, this, JSID_VOID, p, elementType(), v, /* preBarrier = */ false); -} - -void -UnboxedArrayObject::initElementNoTypeChange(size_t index, const Value& v) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - if (UnboxedTypeNeedsPreBarrier(elementType())) - *reinterpret_cast(p) = nullptr; - SetUnboxedValueNoTypeChange(this, p, elementType(), v, /* preBarrier = */ false); -} - -Value -UnboxedArrayObject::getElement(size_t index) -{ - MOZ_ASSERT(index < initializedLength()); - uint8_t* p = elements() + index * elementSize(); - return GetUnboxedValue(p, elementType(), /* maybeUninitialized = */ false); -} - -/* static */ void -UnboxedArrayObject::trace(JSTracer* trc, JSObject* obj) -{ - JSValueType type = obj->as().elementType(); - if (!UnboxedTypeNeedsPreBarrier(type)) - return; - - MOZ_ASSERT(obj->as().elementSize() == sizeof(uintptr_t)); - size_t initlen = obj->as().initializedLength(); - void** elements = reinterpret_cast(obj->as().elements()); - - switch (type) { - case JSVAL_TYPE_OBJECT: - for (size_t i = 0; i < initlen; i++) { - GCPtrObject* heap = reinterpret_cast(elements + i); - TraceNullableEdge(trc, heap, "unboxed_object"); - } - break; - - case JSVAL_TYPE_STRING: - for (size_t i = 0; i < initlen; i++) { - GCPtrString* heap = reinterpret_cast(elements + i); - TraceEdge(trc, heap, "unboxed_string"); - } - break; - - default: - MOZ_CRASH(); - } -} - -/* static */ void -UnboxedArrayObject::objectMoved(JSObject* obj, const JSObject* old) -{ - UnboxedArrayObject& dst = obj->as(); - const UnboxedArrayObject& src = old->as(); - - // Fix up possible inline data pointer. - if (src.hasInlineElements()) - dst.setInlineElements(); -} - -/* static */ void -UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj) -{ - MOZ_ASSERT(!IsInsideNursery(obj)); - if (!obj->as().hasInlineElements()) - js_free(obj->as().elements()); -} - -/* static */ size_t -UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, - gc::AllocKind allocKind) -{ - UnboxedArrayObject* ndst = &dst->as(); - UnboxedArrayObject* nsrc = &src->as(); - MOZ_ASSERT(ndst->elements() == nsrc->elements()); - - Nursery& nursery = trc->runtime()->gc.nursery; - - if (!nursery.isInside(nsrc->elements())) { - nursery.removeMallocedBuffer(nsrc->elements()); - return 0; - } - - // Determine if we can use inline data for the target array. If this is - // possible, the nursery will have picked an allocation size that is large - // enough. - size_t nbytes = nsrc->capacity() * nsrc->elementSize(); - if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) { - ndst->setInlineElements(); - } else { - MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0); - - AutoEnterOOMUnsafeRegion oomUnsafe; - uint8_t* data = nsrc->zone()->pod_malloc(nbytes); - if (!data) - oomUnsafe.crash("Failed to allocate unboxed array elements while tenuring."); - ndst->elements_ = data; - } - - PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize()); - - // Set a forwarding pointer for the element buffers in case they were - // preserved on the stack by Ion. - bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t); - nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct); - - return ndst->hasInlineElements() ? 0 : nbytes; -} - -// Possible capacities for unboxed arrays. Some of these capacities might seem -// a little weird, but were chosen to allow the inline data of objects of each -// size to be fully utilized for arrays of the various types on both 32 bit and -// 64 bit platforms. -// -// To find the possible inline capacities, the following script was used: -// -// var fixedSlotCapacities = [0, 2, 4, 8, 12, 16]; -// var dataSizes = [1, 4, 8]; -// var header32 = 4 * 2 + 4 * 2; -// var header64 = 8 * 2 + 4 * 2; -// -// for (var i = 0; i < fixedSlotCapacities.length; i++) { -// var nfixed = fixedSlotCapacities[i]; -// var size32 = 4 * 4 + 8 * nfixed - header32; -// var size64 = 8 * 4 + 8 * nfixed - header64; -// for (var j = 0; j < dataSizes.length; j++) { -// print(size32 / dataSizes[j]); -// print(size64 / dataSizes[j]); -// } -// } -// -/* static */ const uint32_t -UnboxedArrayObject::CapacityArray[] = { - UINT32_MAX, // For CapacityMatchesLengthIndex. - 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 24, 26, 32, 34, 40, 64, 72, 96, 104, 128, 136, - 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, - 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608, 9437184, 11534336, - 13631488, 15728640, 17825792, 20971520, 24117248, 27262976, 31457280, 35651584, 40894464, - 46137344, 52428800, 59768832, MaximumCapacity -}; - -static const uint32_t -Pow2CapacityIndexes[] = { - 2, // 1 - 3, // 2 - 5, // 4 - 8, // 8 - 13, // 16 - 18, // 32 - 21, // 64 - 25, // 128 - 27, // 256 - 28, // 512 - 29, // 1024 - 30, // 2048 - 31, // 4096 - 32, // 8192 - 33, // 16384 - 34, // 32768 - 35, // 65536 - 36, // 131072 - 37, // 262144 - 38, // 524288 - 39 // 1048576 -}; - -static const uint32_t MebiCapacityIndex = 39; - -/* static */ uint32_t -UnboxedArrayObject::chooseCapacityIndex(uint32_t capacity, uint32_t length) -{ - // Note: the structure and behavior of this method follow along with - // NativeObject::goodAllocated. Changes to the allocation strategy in one - // should generally be matched by the other. - - // Make sure we have enough space to store all possible values for the capacity index. - // This ought to be a static_assert, but MSVC doesn't like that. - MOZ_ASSERT(mozilla::ArrayLength(CapacityArray) - 1 <= (CapacityMask >> CapacityShift)); - - // The caller should have ensured the capacity is possible for an unboxed array. - MOZ_ASSERT(capacity <= MaximumCapacity); - - static const uint32_t Mebi = 1024 * 1024; - - if (capacity <= Mebi) { - capacity = mozilla::RoundUpPow2(capacity); - - // When the required capacity is close to the array length, then round - // up to the array length itself, as for NativeObject. - if (length >= capacity && capacity > (length / 3) * 2) - return CapacityMatchesLengthIndex; - - if (capacity < MinimumDynamicCapacity) - capacity = MinimumDynamicCapacity; - - uint32_t bit = mozilla::FloorLog2Size(capacity); - MOZ_ASSERT(capacity == uint32_t(1 << bit)); - MOZ_ASSERT(bit <= 20); - MOZ_ASSERT(mozilla::ArrayLength(Pow2CapacityIndexes) == 21); - - uint32_t index = Pow2CapacityIndexes[bit]; - MOZ_ASSERT(CapacityArray[index] == capacity); - - return index; - } - - MOZ_ASSERT(CapacityArray[MebiCapacityIndex] == Mebi); - - for (uint32_t i = MebiCapacityIndex + 1;; i++) { - if (CapacityArray[i] >= capacity) - return i; - } - - MOZ_CRASH("Invalid capacity"); -} - -/* static */ uint32_t -UnboxedArrayObject::exactCapacityIndex(uint32_t capacity) -{ - for (size_t i = CapacityMatchesLengthIndex + 1; i < ArrayLength(CapacityArray); i++) { - if (CapacityArray[i] == capacity) - return i; - } - MOZ_CRASH(); -} - -bool -UnboxedArrayObject::growElements(ExclusiveContext* cx, size_t cap) -{ - // The caller should have checked if this capacity is possible for an - // unboxed array, so the only way this call can fail is from OOM. - MOZ_ASSERT(cap <= MaximumCapacity); - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, length()); - uint32_t newCapacity = computeCapacity(newCapacityIndex, length()); - - MOZ_ASSERT(oldCapacity < cap); - MOZ_ASSERT(cap <= newCapacity); - - // The allocation size computation below cannot have integer overflows. - JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double)); - - uint8_t* newElements; - if (hasInlineElements()) { - newElements = AllocateObjectBuffer(cx, this, newCapacity * elementSize()); - if (!newElements) - return false; - js_memcpy(newElements, elements(), initializedLength() * elementSize()); - } else { - newElements = ReallocateObjectBuffer(cx, this, elements(), - oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return false; - } - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); - - return true; -} - -void -UnboxedArrayObject::shrinkElements(ExclusiveContext* cx, size_t cap) -{ - if (hasInlineElements()) - return; - - uint32_t oldCapacity = capacity(); - uint32_t newCapacityIndex = chooseCapacityIndex(cap, 0); - uint32_t newCapacity = computeCapacity(newCapacityIndex, 0); - - MOZ_ASSERT(cap < oldCapacity); - MOZ_ASSERT(cap <= newCapacity); - - if (newCapacity >= oldCapacity) - return; - - uint8_t* newElements = ReallocateObjectBuffer(cx, this, elements(), - oldCapacity * elementSize(), - newCapacity * elementSize()); - if (!newElements) - return; - - elements_ = newElements; - setCapacityIndex(newCapacityIndex); -} - -bool -UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id) -{ - if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength()) - return true; - if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length) - return true; - return false; -} - -/* static */ bool -UnboxedArrayObject::obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp) -{ - if (obj->as().containsProperty(cx, id)) { - MarkNonNativePropertyFound(propp); - objp.set(obj); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - objp.set(nullptr); - propp.set(nullptr); - return true; - } - - return LookupProperty(cx, proto, id, objp, propp); -} - -/* static */ bool -UnboxedArrayObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result) -{ - if (JSID_IS_INT(id) && !desc.getter() && !desc.setter() && desc.attributes() == JSPROP_ENUMERATE) { - UnboxedArrayObject* nobj = &obj->as(); - - uint32_t index = JSID_TO_INT(id); - if (index < nobj->initializedLength()) { - if (nobj->setElement(cx, index, desc.value())) - return result.succeed(); - } else if (index == nobj->initializedLength() && index < MaximumCapacity) { - if (nobj->initializedLength() == nobj->capacity()) { - if (!nobj->growElements(cx, index + 1)) - return false; - } - nobj->setInitializedLength(index + 1); - if (nobj->initElement(cx, index, desc.value())) { - if (nobj->length() <= index) - nobj->setLengthInt32(index + 1); - return result.succeed(); - } - nobj->setInitializedLengthNoBarrier(index); - } - } - - if (!convertToNative(cx, obj)) - return false; - - return DefineProperty(cx, obj, id, desc, result); -} - -/* static */ bool -UnboxedArrayObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp) -{ - if (obj->as().containsProperty(cx, id)) { - *foundp = true; - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - *foundp = false; - return true; - } - - return HasProperty(cx, proto, id, foundp); -} - -/* static */ bool -UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp) -{ - if (obj->as().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) - vp.set(obj->as().getElement(JSID_TO_INT(id))); - else - vp.set(Int32Value(obj->as().length())); - return true; - } - - RootedObject proto(cx, obj->staticPrototype()); - if (!proto) { - vp.setUndefined(); - return true; - } - - return GetProperty(cx, proto, receiver, id, vp); -} - -/* static */ bool -UnboxedArrayObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result) -{ - if (obj->as().containsProperty(cx, id)) { - if (receiver.isObject() && obj == &receiver.toObject()) { - if (JSID_IS_INT(id)) { - if (obj->as().setElement(cx, JSID_TO_INT(id), v)) - return result.succeed(); - } else { - uint32_t len; - if (!CanonicalizeArrayLengthValue(cx, v, &len)) - return false; - UnboxedArrayObject* nobj = &obj->as(); - if (len < nobj->initializedLength()) { - nobj->setInitializedLength(len); - nobj->shrinkElements(cx, len); - } - nobj->setLength(cx, len); - return result.succeed(); - } - - if (!convertToNative(cx, obj)) - return false; - return SetProperty(cx, obj, id, v, receiver, result); - } - - return SetPropertyByDefining(cx, id, v, receiver, result); - } - - return SetPropertyOnProto(cx, obj, id, v, receiver, result); -} - -/* static */ bool -UnboxedArrayObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc) -{ - if (obj->as().containsProperty(cx, id)) { - if (JSID_IS_INT(id)) { - desc.value().set(obj->as().getElement(JSID_TO_INT(id))); - desc.setAttributes(JSPROP_ENUMERATE); - } else { - desc.value().set(Int32Value(obj->as().length())); - desc.setAttributes(JSPROP_PERMANENT); - } - desc.object().set(obj); - return true; - } - - desc.object().set(nullptr); - return true; -} - -/* static */ bool -UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result) -{ - if (obj->as().containsProperty(cx, id)) { - size_t initlen = obj->as().initializedLength(); - if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) { - obj->as().setInitializedLength(initlen - 1); - obj->as().shrinkElements(cx, initlen - 1); - return result.succeed(); - } - } - - if (!convertToNative(cx, obj)) - return false; - return DeleteProperty(cx, obj, id, result); -} - -/* static */ bool -UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable) -{ - if (!convertToNative(cx, obj)) - return false; - return WatchProperty(cx, obj, id, callable); -} - -/* static */ bool -UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly) -{ - for (size_t i = 0; i < obj->as().initializedLength(); i++) { - if (!properties.append(INT_TO_JSID(i))) - return false; - } - - if (!enumerableOnly && !properties.append(NameToId(cx->names().length))) - return false; - - return true; -} - -static const ClassOps UnboxedArrayObjectClassOps = { - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - UnboxedArrayObject::finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - UnboxedArrayObject::trace, -}; - -static const ClassExtension UnboxedArrayObjectClassExtension = { - nullptr, /* weakmapKeyDelegateOp */ - UnboxedArrayObject::objectMoved -}; - -static const ObjectOps UnboxedArrayObjectObjectOps = { - UnboxedArrayObject::obj_lookupProperty, - UnboxedArrayObject::obj_defineProperty, - UnboxedArrayObject::obj_hasProperty, - UnboxedArrayObject::obj_getProperty, - UnboxedArrayObject::obj_setProperty, - UnboxedArrayObject::obj_getOwnPropertyDescriptor, - UnboxedArrayObject::obj_deleteProperty, - UnboxedArrayObject::obj_watch, - nullptr, /* No unwatch needed, as watch() converts the object to native */ - nullptr, /* getElements */ - UnboxedArrayObject::obj_enumerate, - nullptr /* funToString */ -}; - -const Class UnboxedArrayObject::class_ = { - "Array", - Class::NON_NATIVE | - JSCLASS_SKIP_NURSERY_FINALIZE | - JSCLASS_BACKGROUND_FINALIZE, - &UnboxedArrayObjectClassOps, - JS_NULL_CLASS_SPEC, - &UnboxedArrayObjectClassExtension, - &UnboxedArrayObjectObjectOps -}; - -///////////////////////////////////////////////////////////////////// -// API -///////////////////////////////////////////////////////////////////// - -static bool -UnboxedTypeIncludes(JSValueType supertype, JSValueType subtype) -{ - if (supertype == JSVAL_TYPE_DOUBLE && subtype == JSVAL_TYPE_INT32) - return true; - if (supertype == JSVAL_TYPE_OBJECT && subtype == JSVAL_TYPE_NULL) - return true; - return false; -} - -static bool -CombineUnboxedTypes(const Value& value, JSValueType* existing) -{ - JSValueType type = value.isDouble() ? JSVAL_TYPE_DOUBLE : value.extractNonDoubleType(); - - if (*existing == JSVAL_TYPE_MAGIC || *existing == type || UnboxedTypeIncludes(type, *existing)) { - *existing = type; - return true; - } - if (UnboxedTypeIncludes(*existing, type)) - return true; - return false; -} - -// Return whether the property names and types in layout are a subset of the -// specified vector. -static bool -PropertiesAreSuperset(const UnboxedLayout::PropertyVector& properties, UnboxedLayout* layout) -{ - for (size_t i = 0; i < layout->properties().length(); i++) { - const UnboxedLayout::Property& layoutProperty = layout->properties()[i]; - bool found = false; - for (size_t j = 0; j < properties.length(); j++) { - if (layoutProperty.name == properties[j].name) { - found = (layoutProperty.type == properties[j].type); - break; - } - } - if (!found) - return false; - } - return true; -} - -static bool -CombinePlainObjectProperties(PlainObject* obj, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) -{ - // All preliminary objects must have been created with enough space to - // fill in their unboxed data inline. This is ensured either by using - // the largest allocation kind (which limits the maximum size of an - // unboxed object), or by using an allocation kind that covers all - // properties in the template, as the space used by unboxed properties - // is less than or equal to that used by boxed properties. - MOZ_ASSERT(gc::GetGCKindSlots(obj->asTenured().getAllocKind()) >= - Min(NativeObject::MAX_FIXED_SLOTS, templateShape->slotSpan())); - - if (obj->lastProperty() != templateShape || obj->hasDynamicElements()) { - // Only use an unboxed representation if all created objects match - // the template shape exactly. - return false; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - Value val = obj->getSlot(i); - - JSValueType& existing = properties[i].type; - if (!CombineUnboxedTypes(val, &existing)) - return false; - } - - return true; -} - -static bool -CombineArrayObjectElements(ExclusiveContext* cx, ArrayObject* obj, JSValueType* elementType) -{ - if (obj->inDictionaryMode() || - obj->lastProperty()->propid() != AtomToId(cx->names().length) || - !obj->lastProperty()->previous()->isEmptyShape()) - { - // Only use an unboxed representation if the object has no properties. - return false; - } - - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - Value val = obj->getDenseElement(i); - - // For now, unboxed arrays cannot have holes. - if (val.isMagic(JS_ELEMENTS_HOLE)) - return false; - - if (!CombineUnboxedTypes(val, elementType)) - return false; - } - - return true; -} - -static size_t -ComputePlainObjectLayout(ExclusiveContext* cx, Shape* templateShape, - UnboxedLayout::PropertyVector& properties) -{ - // Fill in the names for all the object's properties. - for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { - size_t slot = r.front().slot(); - MOZ_ASSERT(!properties[slot].name); - properties[slot].name = JSID_TO_ATOM(r.front().propid())->asPropertyName(); - } - - // Fill in all the unboxed object's property offsets. - uint32_t offset = 0; - - // Search for an existing unboxed layout which is a subset of this one. - // If there are multiple such layouts, use the largest one. If we're able - // to find such a layout, use the same property offsets for the shared - // properties, which will allow us to generate better code if the objects - // have a subtype/supertype relation and are accessed at common sites. - UnboxedLayout* bestExisting = nullptr; - for (UnboxedLayout* existing : cx->compartment()->unboxedLayouts) { - if (PropertiesAreSuperset(properties, existing)) { - if (!bestExisting || - existing->properties().length() > bestExisting->properties().length()) - { - bestExisting = existing; - } - } - } - if (bestExisting) { - for (size_t i = 0; i < bestExisting->properties().length(); i++) { - const UnboxedLayout::Property& existingProperty = bestExisting->properties()[i]; - for (size_t j = 0; j < templateShape->slotSpan(); j++) { - if (existingProperty.name == properties[j].name) { - MOZ_ASSERT(existingProperty.type == properties[j].type); - properties[j].offset = existingProperty.offset; - } - } - } - offset = bestExisting->size(); - } - - // Order remaining properties from the largest down for the best space - // utilization. - static const size_t typeSizes[] = { 8, 4, 1 }; - - for (size_t i = 0; i < ArrayLength(typeSizes); i++) { - size_t size = typeSizes[i]; - for (size_t j = 0; j < templateShape->slotSpan(); j++) { - if (properties[j].offset != UINT32_MAX) - continue; - JSValueType type = properties[j].type; - if (UnboxedTypeSize(type) == size) { - offset = JS_ROUNDUP(offset, size); - properties[j].offset = offset; - offset += size; - } - } - } - - // The final offset is the amount of data needed by the object. - return offset; -} - -static bool -SetLayoutTraceList(ExclusiveContext* cx, UnboxedLayout* layout) -{ - // Figure out the offsets of any objects or string properties. - Vector objectOffsets, stringOffsets; - for (size_t i = 0; i < layout->properties().length(); i++) { - const UnboxedLayout::Property& property = layout->properties()[i]; - MOZ_ASSERT(property.offset != UINT32_MAX); - if (property.type == JSVAL_TYPE_OBJECT) { - if (!objectOffsets.append(property.offset)) - return false; - } else if (property.type == JSVAL_TYPE_STRING) { - if (!stringOffsets.append(property.offset)) - return false; - } - } - - // Construct the layout's trace list. - if (!objectOffsets.empty() || !stringOffsets.empty()) { - Vector entries; - if (!entries.appendAll(stringOffsets) || - !entries.append(-1) || - !entries.appendAll(objectOffsets) || - !entries.append(-1) || - !entries.append(-1)) - { - return false; - } - int32_t* traceList = cx->zone()->pod_malloc(entries.length()); - if (!traceList) - return false; - PodCopy(traceList, entries.begin(), entries.length()); - layout->setTraceList(traceList); - } - - return true; -} - -static inline Value -NextValue(Handle> values, size_t* valueCursor) -{ - return values[(*valueCursor)++]; -} - -static bool -GetValuesFromPreliminaryArrayObject(ArrayObject* obj, MutableHandle> values) -{ - if (!values.append(Int32Value(obj->length()))) - return false; - if (!values.append(Int32Value(obj->getDenseInitializedLength()))) - return false; - for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) { - if (!values.append(obj->getDenseElement(i))) - return false; - } - return true; -} - -void -UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, - Handle> values, size_t* valueCursor) -{ - MOZ_ASSERT(CapacityArray[1] == 0); - setCapacityIndex(1); - setInitializedLengthNoBarrier(0); - setInlineElements(); - - setLength(cx, NextValue(values, valueCursor).toInt32()); - - int32_t initlen = NextValue(values, valueCursor).toInt32(); - if (!initlen) - return; - - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!growElements(cx, initlen)) - oomUnsafe.crash("UnboxedArrayObject::fillAfterConvert"); - - setInitializedLength(initlen); - - for (size_t i = 0; i < size_t(initlen); i++) - JS_ALWAYS_TRUE(initElement(cx, i, NextValue(values, valueCursor))); -} - -static bool -GetValuesFromPreliminaryPlainObject(PlainObject* obj, MutableHandle> values) -{ - for (size_t i = 0; i < obj->slotSpan(); i++) { - if (!values.append(obj->getSlot(i))) - return false; - } - return true; -} - -void -UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, - Handle> values, size_t* valueCursor) -{ - initExpando(); - memset(data(), 0, layout().size()); - for (size_t i = 0; i < layout().properties().length(); i++) - JS_ALWAYS_TRUE(setValue(cx, layout().properties()[i], NextValue(values, valueCursor))); -} - -bool -js::TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects) -{ - bool isArray = !templateShape; - - // Unboxed arrays are nightly only for now. The getenv() call will be - // removed when they are on by default. See bug 1153266. - if (isArray) { -#ifdef NIGHTLY_BUILD - if (!getenv("JS_OPTION_USE_UNBOXED_ARRAYS")) { - if (!cx->options().unboxedArrays()) - return true; - } -#else - return true; -#endif - } else { - if (jit::JitOptions.disableUnboxedObjects) - return true; - } - - MOZ_ASSERT_IF(templateShape, !templateShape->getObjectFlags()); - - if (group->runtimeFromAnyThread()->isSelfHostingGlobal(cx->global())) - return true; - - if (!isArray && templateShape->slotSpan() == 0) - return true; - - UnboxedLayout::PropertyVector properties; - if (!isArray) { - if (!properties.appendN(UnboxedLayout::Property(), templateShape->slotSpan())) - return false; - } - JSValueType elementType = JSVAL_TYPE_MAGIC; - - size_t objectCount = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - if (obj->isSingleton() || obj->group() != group) - return true; - - objectCount++; - - if (isArray) { - if (!CombineArrayObjectElements(cx, &obj->as(), &elementType)) - return true; - } else { - if (!CombinePlainObjectProperties(&obj->as(), templateShape, properties)) - return true; - } - } - - size_t layoutSize = 0; - if (isArray) { - // Don't use an unboxed representation if we couldn't determine an - // element type for the objects. - if (UnboxedTypeSize(elementType) == 0) - return true; - } else { - if (objectCount <= 1) { - // If only one of the objects has been created, it is more likely - // to have new properties added later. This heuristic is not used - // for array objects, where we might want an unboxed representation - // even if there is only one large array. - return true; - } - - for (size_t i = 0; i < templateShape->slotSpan(); i++) { - // We can't use an unboxed representation if e.g. all the objects have - // a null value for one of the properties, as we can't decide what type - // it is supposed to have. - if (UnboxedTypeSize(properties[i].type) == 0) - return true; - } - - // Make sure that all properties on the template shape are property - // names, and not indexes. - for (Shape::Range r(templateShape); !r.empty(); r.popFront()) { - jsid id = r.front().propid(); - uint32_t dummy; - if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy)) - return true; - } - - layoutSize = ComputePlainObjectLayout(cx, templateShape, properties); - - // The entire object must be allocatable inline. - if (UnboxedPlainObject::offsetOfData() + layoutSize > JSObject::MAX_BYTE_SIZE) - return true; - } - - UniquePtr& layout = enter.unboxedLayoutToCleanUp; - MOZ_ASSERT(!layout); - layout = group->zone()->make_unique(); - if (!layout) - return false; - - if (isArray) { - layout->initArray(elementType); - } else { - if (!layout->initProperties(properties, layoutSize)) - return false; - - // The unboxedLayouts list only tracks layouts for plain objects. - cx->compartment()->unboxedLayouts.insertFront(layout.get()); - - if (!SetLayoutTraceList(cx, layout.get())) - return false; - } - - // We've determined that all the preliminary objects can use the new layout - // just constructed, so convert the existing group to use the unboxed class, - // and update the preliminary objects to use the new layout. Do the - // fallible stuff first before modifying any objects. - - // Get an empty shape which we can use for the preliminary objects. - const Class* clasp = isArray ? &UnboxedArrayObject::class_ : &UnboxedPlainObject::class_; - Shape* newShape = EmptyShape::getInitialShape(cx, clasp, group->proto(), 0); - if (!newShape) { - cx->recoverFromOutOfMemory(); - return false; - } - - // Accumulate a list of all the values in each preliminary object, and - // update their shapes. - Rooted> values(cx, GCVector(cx)); - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - bool ok; - if (isArray) - ok = GetValuesFromPreliminaryArrayObject(&obj->as(), &values); - else - ok = GetValuesFromPreliminaryPlainObject(&obj->as(), &values); - - if (!ok) { - cx->recoverFromOutOfMemory(); - return false; - } - } - - if (TypeNewScript* newScript = group->newScript()) - layout->setNewScript(newScript); - - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - if (JSObject* obj = objects->get(i)) - obj->as().setLastPropertyMakeNonNative(newShape); - } - - group->setClasp(clasp); - group->setUnboxedLayout(layout.release()); - - size_t valueCursor = 0; - for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { - JSObject* obj = objects->get(i); - if (!obj) - continue; - - if (isArray) - obj->as().fillAfterConvert(cx, values, &valueCursor); - else - obj->as().fillAfterConvert(cx, values, &valueCursor); - } - - MOZ_ASSERT(valueCursor == values.length()); - return true; -} - -DefineBoxedOrUnboxedFunctor6(SetOrExtendBoxedOrUnboxedDenseElements, - ExclusiveContext*, JSObject*, uint32_t, const Value*, uint32_t, - ShouldUpdateTypes); - -DenseElementResult -js::SetOrExtendAnyBoxedOrUnboxedDenseElements(ExclusiveContext* cx, JSObject* obj, - uint32_t start, const Value* vp, uint32_t count, - ShouldUpdateTypes updateTypes) -{ - SetOrExtendBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, start, vp, count, updateTypes); - return CallBoxedOrUnboxedSpecialization(functor, obj); -}; - -DefineBoxedOrUnboxedFunctor5(MoveBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, uint32_t, uint32_t, uint32_t); - -DenseElementResult -js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length); - return CallBoxedOrUnboxedSpecialization(functor, obj); -} - -DefineBoxedOrUnboxedFunctorPair6(CopyBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t); - -DenseElementResult -js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src, - uint32_t dstStart, uint32_t srcStart, uint32_t length) -{ - CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length); - return CallBoxedOrUnboxedSpecialization(functor, dst, src); -} - -DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength, - JSContext*, JSObject*, size_t); - -void -js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen) -{ - SetBoxedOrUnboxedInitializedLengthFunctor functor(cx, obj, initlen); - JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success); -} - -DefineBoxedOrUnboxedFunctor3(EnsureBoxedOrUnboxedDenseElements, - JSContext*, JSObject*, size_t); - -DenseElementResult -js::EnsureAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj, size_t initlen) -{ - EnsureBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, initlen); - return CallBoxedOrUnboxedSpecialization(functor, obj); -} diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h deleted file mode 100644 index ecff8be5b..000000000 --- a/js/src/vm/UnboxedObject.h +++ /dev/null @@ -1,531 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef vm_UnboxedObject_h -#define vm_UnboxedObject_h - -#include "jsgc.h" -#include "jsobj.h" - -#include "vm/Runtime.h" -#include "vm/TypeInference.h" - -namespace js { - -// Memory required for an unboxed value of a given type. Returns zero for types -// which can't be used for unboxed objects. -static inline size_t -UnboxedTypeSize(JSValueType type) -{ - switch (type) { - case JSVAL_TYPE_BOOLEAN: return 1; - case JSVAL_TYPE_INT32: return 4; - case JSVAL_TYPE_DOUBLE: return 8; - case JSVAL_TYPE_STRING: return sizeof(void*); - case JSVAL_TYPE_OBJECT: return sizeof(void*); - default: return 0; - } -} - -static inline bool -UnboxedTypeNeedsPreBarrier(JSValueType type) -{ - return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT; -} - -static inline bool -UnboxedTypeNeedsPostBarrier(JSValueType type) -{ - return type == JSVAL_TYPE_OBJECT; -} - -// Class tracking information specific to unboxed objects. -class UnboxedLayout : public mozilla::LinkedListElement -{ - public: - struct Property { - PropertyName* name; - uint32_t offset; - JSValueType type; - - Property() - : name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC) - {} - }; - - typedef Vector PropertyVector; - - private: - // If objects in this group have ever been converted to native objects, - // these store the corresponding native group and initial shape for such - // objects. Type information for this object is reflected in nativeGroup. - GCPtrObjectGroup nativeGroup_; - GCPtrShape nativeShape_; - - // Any script/pc which the associated group is created for. - GCPtrScript allocationScript_; - jsbytecode* allocationPc_; - - // If nativeGroup is set and this object originally had a TypeNewScript or - // was keyed to an allocation site, this points to the group which replaced - // this one. This link is only needed to keep the replacement group from - // being GC'ed. If it were GC'ed and a new one regenerated later, that new - // group might have a different allocation kind from this group. - GCPtrObjectGroup replacementGroup_; - - // The following members are only used for unboxed plain objects. - - // All properties on objects with this layout, in enumeration order. - PropertyVector properties_; - - // Byte size of the data for objects with this layout. - size_t size_; - - // Any 'new' script information associated with this layout. - TypeNewScript* newScript_; - - // List for use in tracing objects with this layout. This has the same - // structure as the trace list on a TypeDescr. - int32_t* traceList_; - - // If this layout has been used to construct script or JSON constant - // objects, this code might be filled in to more quickly fill in objects - // from an array of values. - GCPtrJitCode constructorCode_; - - // The following members are only used for unboxed arrays. - - // The type of array elements. - JSValueType elementType_; - - public: - UnboxedLayout() - : nativeGroup_(nullptr), nativeShape_(nullptr), - allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr), - size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr), - elementType_(JSVAL_TYPE_MAGIC) - {} - - bool initProperties(const PropertyVector& properties, size_t size) { - size_ = size; - return properties_.appendAll(properties); - } - - void initArray(JSValueType elementType) { - elementType_ = elementType; - } - - ~UnboxedLayout() { - if (newScript_) - newScript_->clear(); - js_delete(newScript_); - js_free(traceList_); - - nativeGroup_.init(nullptr); - nativeShape_.init(nullptr); - replacementGroup_.init(nullptr); - constructorCode_.init(nullptr); - } - - bool isArray() const { - return elementType_ != JSVAL_TYPE_MAGIC; - } - - void detachFromCompartment(); - - const PropertyVector& properties() const { - return properties_; - } - - TypeNewScript* newScript() const { - return newScript_; - } - - void setNewScript(TypeNewScript* newScript, bool writeBarrier = true); - - JSScript* allocationScript() const { - return allocationScript_; - } - - jsbytecode* allocationPc() const { - return allocationPc_; - } - - void setAllocationSite(JSScript* script, jsbytecode* pc) { - allocationScript_ = script; - allocationPc_ = pc; - } - - const int32_t* traceList() const { - return traceList_; - } - - void setTraceList(int32_t* traceList) { - traceList_ = traceList; - } - - const Property* lookup(JSAtom* atom) const { - for (size_t i = 0; i < properties_.length(); i++) { - if (properties_[i].name == atom) - return &properties_[i]; - } - return nullptr; - } - - const Property* lookup(jsid id) const { - if (JSID_IS_STRING(id)) - return lookup(JSID_TO_ATOM(id)); - return nullptr; - } - - size_t size() const { - return size_; - } - - ObjectGroup* nativeGroup() const { - return nativeGroup_; - } - - Shape* nativeShape() const { - return nativeShape_; - } - - jit::JitCode* constructorCode() const { - return constructorCode_; - } - - void setConstructorCode(jit::JitCode* code) { - constructorCode_ = code; - } - - JSValueType elementType() const { - return elementType_; - } - - inline gc::AllocKind getAllocKind() const; - - void trace(JSTracer* trc); - - size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); - - static bool makeNativeGroup(JSContext* cx, ObjectGroup* group); - static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group); -}; - -// Class for expando objects holding extra properties given to an unboxed plain -// object. These objects behave identically to normal native plain objects, and -// have a separate Class to distinguish them for memory usage reporting. -class UnboxedExpandoObject : public NativeObject -{ - public: - static const Class class_; -}; - -// Class for a plain object using an unboxed representation. The physical -// layout of these objects is identical to that of an InlineTypedObject, though -// these objects use an UnboxedLayout instead of a TypeDescr to keep track of -// how their properties are stored. -class UnboxedPlainObject : public JSObject -{ - // Optional object which stores extra properties on this object. This is - // not automatically barriered to avoid problems if the object is converted - // to a native. See ensureExpando(). - UnboxedExpandoObject* expando_; - - // Start of the inline data, which immediately follows the group and extra properties. - uint8_t data_[1]; - - public: - static const Class class_; - - static bool obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp); - - static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result); - - static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - - static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp); - - static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result); - - static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc); - - static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result); - - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly); - static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - - inline const UnboxedLayout& layout() const; - - const UnboxedLayout& layoutDontCheckGeneration() const { - return group()->unboxedLayoutDontCheckGeneration(); - } - - uint8_t* data() { - return &data_[0]; - } - - UnboxedExpandoObject* maybeExpando() const { - return expando_; - } - - void initExpando() { - expando_ = nullptr; - } - - // For use during GC. - JSObject** addressOfExpando() { - return reinterpret_cast(&expando_); - } - - bool containsUnboxedOrExpandoProperty(ExclusiveContext* cx, jsid id) const; - - static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle obj); - - bool setValue(ExclusiveContext* cx, const UnboxedLayout::Property& property, const Value& v); - Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false); - - static bool convertToNative(JSContext* cx, JSObject* obj); - static UnboxedPlainObject* create(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind); - static JSObject* createWithProperties(ExclusiveContext* cx, HandleObjectGroup group, - NewObjectKind newKind, IdValuePair* properties); - - void fillAfterConvert(ExclusiveContext* cx, - Handle> values, size_t* valueCursor); - - static void trace(JSTracer* trc, JSObject* object); - - static size_t offsetOfExpando() { - return offsetof(UnboxedPlainObject, expando_); - } - - static size_t offsetOfData() { - return offsetof(UnboxedPlainObject, data_[0]); - } -}; - -// Try to construct an UnboxedLayout for each of the preliminary objects, -// provided they all match the template shape. If successful, converts the -// preliminary objects and their group to the new unboxed representation. -bool -TryConvertToUnboxedLayout(ExclusiveContext* cx, AutoEnterAnalysis& enter, Shape* templateShape, - ObjectGroup* group, PreliminaryObjectArray* objects); - -inline gc::AllocKind -UnboxedLayout::getAllocKind() const -{ - MOZ_ASSERT(size()); - return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size()); -} - -// Class for an array object using an unboxed representation. -class UnboxedArrayObject : public JSObject -{ - // Elements pointer for the object. - uint8_t* elements_; - - // The nominal array length. This always fits in an int32_t. - uint32_t length_; - - // Value indicating the allocated capacity and initialized length of the - // array. The top CapacityBits bits are an index into CapacityArray, which - // indicates the elements capacity. The low InitializedLengthBits store the - // initialized length of the array. - uint32_t capacityIndexAndInitializedLength_; - - // If the elements are inline, they will point here. - uint8_t inlineElements_[1]; - - public: - static const uint32_t CapacityBits = 6; - static const uint32_t CapacityShift = 26; - - static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift; - static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1; - - static const uint32_t MaximumCapacity = InitializedLengthMask; - static const uint32_t MinimumDynamicCapacity = 8; - - static const uint32_t CapacityArray[]; - - // Capacity index which indicates the array's length is also its capacity. - static const uint32_t CapacityMatchesLengthIndex = 0; - - private: - static inline uint32_t computeCapacity(uint32_t index, uint32_t length) { - if (index == CapacityMatchesLengthIndex) - return length; - return CapacityArray[index]; - } - - static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length); - static uint32_t exactCapacityIndex(uint32_t capacity); - - public: - static const Class class_; - - static bool obj_lookupProperty(JSContext* cx, HandleObject obj, - HandleId id, MutableHandleObject objp, - MutableHandleShape propp); - - static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle desc, - ObjectOpResult& result); - - static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); - - static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver, - HandleId id, MutableHandleValue vp); - - static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - HandleValue receiver, ObjectOpResult& result); - - static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, - MutableHandle desc); - - static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, - ObjectOpResult& result); - - static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, - bool enumerableOnly); - static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); - - inline const UnboxedLayout& layout() const; - - const UnboxedLayout& layoutDontCheckGeneration() const { - return group()->unboxedLayoutDontCheckGeneration(); - } - - JSValueType elementType() const { - return layoutDontCheckGeneration().elementType(); - } - - uint32_t elementSize() const { - return UnboxedTypeSize(elementType()); - } - - static bool convertToNative(JSContext* cx, JSObject* obj); - static UnboxedArrayObject* create(ExclusiveContext* cx, HandleObjectGroup group, - uint32_t length, NewObjectKind newKind, - uint32_t maxLength = MaximumCapacity); - - static bool convertToNativeWithGroup(ExclusiveContext* cx, JSObject* obj, - ObjectGroup* group, Shape* shape); - bool convertInt32ToDouble(ExclusiveContext* cx, ObjectGroup* group); - - void fillAfterConvert(ExclusiveContext* cx, - Handle> values, size_t* valueCursor); - - static void trace(JSTracer* trc, JSObject* object); - static void objectMoved(JSObject* obj, const JSObject* old); - static void finalize(FreeOp* fop, JSObject* obj); - - static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src, - gc::AllocKind allocKind); - - uint8_t* elements() { - return elements_; - } - - bool hasInlineElements() const { - return elements_ == &inlineElements_[0]; - } - - uint32_t length() const { - return length_; - } - - uint32_t initializedLength() const { - return capacityIndexAndInitializedLength_ & InitializedLengthMask; - } - - uint32_t capacityIndex() const { - return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift; - } - - uint32_t capacity() const { - return computeCapacity(capacityIndex(), length()); - } - - bool containsProperty(ExclusiveContext* cx, jsid id); - - bool setElement(ExclusiveContext* cx, size_t index, const Value& v); - bool initElement(ExclusiveContext* cx, size_t index, const Value& v); - void initElementNoTypeChange(size_t index, const Value& v); - Value getElement(size_t index); - - template inline bool setElementSpecific(ExclusiveContext* cx, size_t index, - const Value& v); - template inline void setElementNoTypeChangeSpecific(size_t index, const Value& v); - template inline bool initElementSpecific(ExclusiveContext* cx, size_t index, - const Value& v); - template inline void initElementNoTypeChangeSpecific(size_t index, const Value& v); - template inline Value getElementSpecific(size_t index); - template inline void triggerPreBarrier(size_t index); - - bool growElements(ExclusiveContext* cx, size_t cap); - void shrinkElements(ExclusiveContext* cx, size_t cap); - - static uint32_t offsetOfElements() { - return offsetof(UnboxedArrayObject, elements_); - } - static uint32_t offsetOfLength() { - return offsetof(UnboxedArrayObject, length_); - } - static uint32_t offsetOfCapacityIndexAndInitializedLength() { - return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_); - } - static uint32_t offsetOfInlineElements() { - return offsetof(UnboxedArrayObject, inlineElements_); - } - - void setLengthInt32(uint32_t length) { - MOZ_ASSERT(length <= INT32_MAX); - length_ = length; - } - - inline void setLength(ExclusiveContext* cx, uint32_t len); - inline void setInitializedLength(uint32_t initlen); - - inline void setInitializedLengthNoBarrier(uint32_t initlen) { - MOZ_ASSERT(initlen <= InitializedLengthMask); - capacityIndexAndInitializedLength_ = - (capacityIndexAndInitializedLength_ & CapacityMask) | initlen; - } - - private: - void setInlineElements() { - elements_ = &inlineElements_[0]; - } - - void setCapacityIndex(uint32_t index) { - MOZ_ASSERT(index <= (CapacityMask >> CapacityShift)); - capacityIndexAndInitializedLength_ = - (index << CapacityShift) | initializedLength(); - } -}; - -} // namespace js - -namespace JS { - -template <> -struct DeletePolicy : public js::GCManagedDeletePolicy -{}; - -} /* namespace JS */ - -#endif /* vm_UnboxedObject_h */ diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index bde949a96..0243d80e3 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -1427,8 +1427,6 @@ ReloadPrefsCallback(const char* pref, void* data) bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict"); - bool unboxedObjects = Preferences::GetBool(JS_OPTIONS_DOT_STR "unboxed_objects"); - sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory"); #ifdef DEBUG @@ -1457,8 +1455,6 @@ ReloadPrefsCallback(const char* pref, void* data) useBaselineEager ? 0 : -1); JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER, useIonEager ? 0 : -1); - JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_UNBOXED_OBJECTS, - unboxedObjects); } XPCJSContext::~XPCJSContext() diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index ae455cadf..06b79f443 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1268,7 +1268,6 @@ pref("javascript.options.strict", false); #ifdef DEBUG pref("javascript.options.strict.debug", false); #endif -pref("javascript.options.unboxed_objects", false); pref("javascript.options.baselinejit", true); pref("javascript.options.ion", true); pref("javascript.options.asmjs", true);