From 3538814b9231fb27514881e6eb7bed3bdc9d1ee6 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sun, 19 May 2019 23:11:17 +0200 Subject: [PATCH 01/18] Implement array.flat and array.flatMap Self-hosted implementation that adds both functions and adds them to @@unscopables as specced in ES2019. Resolves #1095 --- js/src/builtin/Array.js | 108 ++++++++++++++++++++ js/src/jsarray.cpp | 7 ++ js/src/tests/ecma_6/Array/unscopables.js | 2 + js/src/vm/CommonPropertyNames.h | 2 + js/xpconnect/tests/chrome/test_xrayToJS.xul | 2 +- 5 files changed, 120 insertions(+), 1 deletion(-) diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 30e6fb35fb..05fc41bc14 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -1073,6 +1073,114 @@ function ArrayConcat(arg1) { return A; } +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function ArrayFlatMap(mapperFunction/*, thisArg*/) { + // Step 1. + var O = ToObject(this); + + // Step 2. + var sourceLen = ToLength(O.length); + + // Step 3. + if (!IsCallable(mapperFunction)) + ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapperFunction)); + + // Step 4. + var T = arguments.length > 1 ? arguments[1] : undefined; + + // Step 5. + var A = ArraySpeciesCreate(O, 0); + + // Step 6. + FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T); + + // Step 7. + return A; +} + +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function ArrayFlat(/* depth */) { + // Step 1. + var O = ToObject(this); + + // Step 2. + var sourceLen = ToLength(O.length); + + // Step 3. + var depthNum = 1; + + // Step 4. + if (arguments.length > 0 && arguments[0] !== undefined) + depthNum = ToInteger(arguments[0]); + + // Step 5. + var A = ArraySpeciesCreate(O, 0); + + // Step 6. + FlattenIntoArray(A, O, sourceLen, 0, depthNum); + + // Step 7. + return A; +} + +// https://tc39.github.io/proposal-flatMap/ +// January 4, 2019 +function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) { + // Step 1. + var targetIndex = start; + + // Steps 2-3. + for (var sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { + // Steps 3.a-c. + if (sourceIndex in source) { + // Step 3.c.i. + var element = source[sourceIndex]; + + if (mapperFunction) { + // Step 3.c.ii.1. + assert(arguments.length === 7, "thisArg is present"); + + // Step 3.c.ii.2. + element = callContentFunction(mapperFunction, thisArg, element, sourceIndex, source); + } + + // Step 3.c.iii. + var shouldFlatten = false; + + // Step 3.c.iv. + if (depth > 0) { + // Step 3.c.iv.1. + shouldFlatten = IsArray(element); + } + + // Step 3.c.v. + if (shouldFlatten) { + // Step 3.c.v.1. + var elementLen = ToLength(element.length); + + // Step 3.c.v.2. + // Recursive call to walk the depth. + targetIndex = FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1); + } else { + // Step 3.c.vi.1. + if (targetIndex >= MAX_NUMERIC_INDEX) + ThrowTypeError(JSMSG_TOO_LONG_ARRAY); + + // Step 3.c.vi.2. + _DefineDataProperty(target, targetIndex, element); + + // Step 3.c.vi.3. + targetIndex++; + } + } + } + + // Step 4. + return targetIndex; +} + function ArrayStaticConcat(arr, arg1) { if (arguments.length < 1) ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat'); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 7a67c00954..3b4c957dc8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3169,6 +3169,11 @@ static const JSFunctionSpec array_methods[] = { /* ES7 additions */ JS_SELF_HOSTED_FN("includes", "ArrayIncludes", 2,0), + + /* ES2019 additions */ + JS_SELF_HOSTED_FN("flat", "ArrayFlat", 0,0), + JS_SELF_HOSTED_FN("flatMap", "ArrayFlatMap", 1,0), + JS_FS_END }; @@ -3333,6 +3338,8 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto) !DefineProperty(cx, unscopables, cx->names().fill, value) || !DefineProperty(cx, unscopables, cx->names().find, value) || !DefineProperty(cx, unscopables, cx->names().findIndex, value) || + !DefineProperty(cx, unscopables, cx->names().flat, value) || + !DefineProperty(cx, unscopables, cx->names().flatMap, value) || !DefineProperty(cx, unscopables, cx->names().includes, value) || !DefineProperty(cx, unscopables, cx->names().keys, value) || !DefineProperty(cx, unscopables, cx->names().values, value)) diff --git a/js/src/tests/ecma_6/Array/unscopables.js b/js/src/tests/ecma_6/Array/unscopables.js index 6685309a06..56b3aa520a 100644 --- a/js/src/tests/ecma_6/Array/unscopables.js +++ b/js/src/tests/ecma_6/Array/unscopables.js @@ -26,6 +26,8 @@ assertDeepEq(keys, [ "fill", "find", "findIndex", + "flat", + "flatMap", "includes", "keys", "values" diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index e971dc8443..8a36df0836 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -115,6 +115,8 @@ macro(firstDayOfWeek, firstDayOfWeek, "firstDayOfWeek") \ macro(fix, fix, "fix") \ macro(flags, flags, "flags") \ + macro(flat, flat, "flat") \ + macro(flatMap, flatMap, "flatMap") \ macro(float32, float32, "float32") \ macro(Float32x4, Float32x4, "Float32x4") \ macro(float64, float64, "float64") \ diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index ed67a34fe7..73de267a1c 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -198,7 +198,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf", "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find", "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys", - "values", "constructor"]; + "values", "constructor", "flat", "flatMap"]; if (isNightlyBuild) { // ...nothing now } From ee0121726536d983473b5ef8c58e52b85241e301 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Mon, 20 May 2019 02:50:01 +0200 Subject: [PATCH 02/18] =?UTF-8?q?Implement=20Symbol=E2=80=8B.prototype?= =?UTF-8?q?=E2=80=8B.description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #1096 --- js/src/builtin/SymbolObject.cpp | 29 +++++++++++++++++++++++++++++ js/src/builtin/SymbolObject.h | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp index cf48402d69..ae38d5371b 100644 --- a/js/src/builtin/SymbolObject.cpp +++ b/js/src/builtin/SymbolObject.cpp @@ -33,6 +33,7 @@ SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol) } const JSPropertySpec SymbolObject::properties[] = { + JS_PSG("description", descriptionGetter, 0), JS_PS_END }; @@ -227,6 +228,34 @@ SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp) return CallNonGenericMethod(cx, args); } +// ES2019 Stage 4 Draft / November 28, 2018 +// Symbol description accessor +// See: https://tc39.github.io/proposal-Symbol-description/ +bool +SymbolObject::descriptionGetter_impl(JSContext* cx, const CallArgs& args) +{ + // Get symbol object pointer. + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsSymbol(thisv)); + Rooted sym(cx, thisv.isSymbol() + ? thisv.toSymbol() + : thisv.toObject().as().unbox()); + + // Return the symbol's description if present, otherwise return undefined. + if (JSString* str = sym->description()) + args.rval().setString(str); + else + args.rval().setUndefined(); + return true; +} + +bool +SymbolObject::descriptionGetter(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + JSObject* js::InitSymbolClass(JSContext* cx, HandleObject obj) { diff --git a/js/src/builtin/SymbolObject.h b/js/src/builtin/SymbolObject.h index 0f204b494a..e10b42d532 100644 --- a/js/src/builtin/SymbolObject.h +++ b/js/src/builtin/SymbolObject.h @@ -52,6 +52,10 @@ class SymbolObject : public NativeObject static MOZ_MUST_USE bool valueOf(JSContext* cx, unsigned argc, Value* vp); static MOZ_MUST_USE bool toPrimitive(JSContext* cx, unsigned argc, Value* vp); + // Properties defined on Symbol.prototype. + static MOZ_MUST_USE bool descriptionGetter_impl(JSContext* cx, const CallArgs& args); + static MOZ_MUST_USE bool descriptionGetter(JSContext* cx, unsigned argc, Value *vp); + static const JSPropertySpec properties[]; static const JSFunctionSpec methods[]; static const JSFunctionSpec staticMethods[]; From 69e171fb9acc4a352eb9af47c2c5d30180b54669 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Wed, 22 May 2019 19:24:30 +0800 Subject: [PATCH 03/18] Issue #971 - Fix browser.link.open_newwindow functionality in Pale Moon --- application/palemoon/base/content/browser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js index 3f8a584bfa..cc362d23f9 100644 --- a/application/palemoon/base/content/browser.js +++ b/application/palemoon/base/content/browser.js @@ -4481,7 +4481,6 @@ nsBrowserAccess.prototype = { } let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"); - let referrer = aOpener ? makeURI(aOpener.location.href) : null; let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", { triggeringPrincipal: triggeringPrincipal, From 3c6346afa9e16d8ea23c192110f5d2d448ead18a Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Mon, 13 May 2019 15:45:32 +0000 Subject: [PATCH 04/18] Unhook Unboxed Objects option --- js/src/jsapi.cpp | 3 --- js/src/jsapi.h | 25 ++++++++++++------------- js/src/shell/js.cpp | 3 --- js/xpconnect/src/XPCJSContext.cpp | 4 ---- modules/libpref/init/all.js | 1 - 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f78f94dc12..9ee29ffe42 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 1f726f2e54..005d2278e4 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5783,20 +5783,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/shell/js.cpp b/js/src/shell/js.cpp index 8d144417a4..193d8d22b7 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -7276,9 +7276,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op) 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; diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index bde949a960..0243d80e36 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 4b3285641b..f70a78c1cc 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1264,7 +1264,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); From 7b0eb7be8cabb5c0273a5b58be1bb8f71c71acc0 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Tue, 14 May 2019 11:44:11 +0000 Subject: [PATCH 05/18] remove unboxed code chunk (wip1) --- js/src/jit/BaselineInspector.cpp | 31 +-- js/src/jit/BaselineInspector.h | 9 +- js/src/jit/IonBuilder.cpp | 80 +------ js/src/jit/IonBuilder.h | 4 - js/src/jit/MCallOptimize.cpp | 8 +- js/src/vm/UnboxedObject.cpp | 400 ------------------------------- 6 files changed, 25 insertions(+), 507 deletions(-) diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index c9e09bed7a..9c7b88fb22 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -96,13 +96,8 @@ 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); } @@ -170,16 +165,12 @@ GetCacheIRReceiverForUnboxedProperty(ICCacheIR_Monitored* stub, ReceiverGuard* r } 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; @@ -207,7 +198,7 @@ BaselineInspector::maybeInfoForPropertyOp(jsbytecode* pc, ReceiverVector& receiv return true; } - if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) + if (!AddReceiver(receiver, receivers)) return false; stub = stub->next(); @@ -700,14 +691,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 +708,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 +740,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 +761,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 961df18aa2..4a17917982 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); @@ -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/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 54d05cac4e..28fa53aa8d 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -9012,8 +9012,6 @@ IonBuilder::jsop_getelem() } obj = maybeUnboxForPropertyAccess(obj); - if (obj->type() == MIRType::Object) - obj = convertUnboxedObjects(obj); bool emitted = false; @@ -10137,7 +10135,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()); @@ -11496,8 +11494,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); @@ -11939,49 +11935,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) @@ -12163,14 +12116,11 @@ IonBuilder::getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* nam 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 +12144,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 +12161,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 +12328,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; @@ -12692,7 +12636,7 @@ bool IonBuilder::jsop_setprop(PropertyName* name) { MDefinition* value = current->pop(); - MDefinition* obj = convertUnboxedObjects(current->pop()); + MDefinition* obj = current->pop(); bool emitted = false; startTrackingOptimizations(); @@ -12765,10 +12709,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 +12725,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; } @@ -13146,15 +13087,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. @@ -13884,7 +13822,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; diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f24ef30c84..2880c7ea1d 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -1041,7 +1041,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,9 +1058,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, diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 01755094af..41ccd0ca7b 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -615,7 +615,7 @@ 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; @@ -743,7 +743,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)) @@ -822,7 +822,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) @@ -2152,7 +2152,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/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 3018ace677..d7ad91de41 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -1655,227 +1655,12 @@ const Class UnboxedArrayObject::class_ = { // 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) @@ -1901,16 +1686,6 @@ UnboxedArrayObject::fillAfterConvert(ExclusiveContext* cx, 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) @@ -1921,181 +1696,6 @@ UnboxedPlainObject::fillAfterConvert(ExclusiveContext* cx, 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); From fb235220875fbd99ebc1b50dd5017bd1e125ddd0 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Wed, 15 May 2019 02:06:04 +0000 Subject: [PATCH 06/18] Remove initial chunk of Unboxed Objects machinery part 2 --- js/src/vm/TypeInference.cpp | 5 ----- js/src/vm/UnboxedObject.h | 7 ------- 2 files changed, 12 deletions(-) diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index 4775a2dea2..fe89fac9ce 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3577,7 +3577,6 @@ PreliminaryObjectArrayWithTemplate::maybeAnalyze(ExclusiveContext* cx, ObjectGro } } - TryConvertToUnboxedLayout(cx, enter, shape(), group, preliminaryObjects); if (group->maybeUnboxedLayout()) return; @@ -3861,10 +3860,6 @@ 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; diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index ecff8be5b1..779dd14c7e 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -317,13 +317,6 @@ class UnboxedPlainObject : public JSObject } }; -// 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 { From d7714885d11be96638276f6b6179e9a5debec2fc Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Wed, 15 May 2019 13:18:33 +0000 Subject: [PATCH 07/18] Remove Unboxed Objects in ScalarReplacement A note about the Scalar Replacement Changes: The M{Load,Store}Unboxed* instructions in theory could be used to manipulate and analyze typed arrays. However, TypedArrays should already be excluded from eligibility because of the potential for cross-thread sharing in a SharedArrayBuffer world, and so the only support in Scalar Replacement here is for Unboxed Objects, meaning it can all be removed. --- js/src/jit/ScalarReplacement.cpp | 112 ------------------------------- 1 file changed, 112 deletions(-) diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 4614b21627..2065c0371b 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) { From d73dc74bd1ed3cadd2d6aea0241a8dc99e10ee5f Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Wed, 15 May 2019 19:37:25 +0000 Subject: [PATCH 08/18] Remove unboxed objects from GC --- js/src/gc/Marking.cpp | 46 +---------------------------- js/src/gc/Tracer.cpp | 69 ++----------------------------------------- js/src/jsgc.cpp | 6 ---- 3 files changed, 3 insertions(+), 118 deletions(-) diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 3ea4c9d29f..47d2314c1a 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 diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp index 63cd9b08a4..3416464ddf 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/jsgc.cpp b/js/src/jsgc.cpp index 194468c5d7..b2ee8d67b2 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. From 4dd127a34dd15be7d90823929deea973336adaed Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Wed, 15 May 2019 20:56:36 +0000 Subject: [PATCH 09/18] Remove unboxed object code from iteration. --- js/src/jsiter.cpp | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index c1ae5dc159..004c7fc0d5 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; } From 96c56fa091f8f949c11a63cd71b0299a43270f77 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 16 May 2019 00:50:49 +0000 Subject: [PATCH 10/18] Remove array header --- js/src/jsarray.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3b4c957dc8..0fed05835f 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; From 37315e4898740d6fc2850b01b9fe7b60290ba416 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 16 May 2019 13:13:36 +0000 Subject: [PATCH 11/18] Remove Unboxed Objects from vm/ Part 1 + fix deprot --- js/src/jit/OptimizationTracking.cpp | 2 ++ js/src/jit/SharedIC.cpp | 1 + js/src/jsarray.cpp | 1 + js/src/jsobj.cpp | 1 + js/src/vm/Interpreter-inl.h | 13 ++++--------- js/src/vm/Interpreter.cpp | 7 +------ 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/js/src/jit/OptimizationTracking.cpp b/js/src/jit/OptimizationTracking.cpp index 308def0411..b42634d436 100644 --- a/js/src/jit/OptimizationTracking.cpp +++ b/js/src/jit/OptimizationTracking.cpp @@ -15,9 +15,11 @@ #include "jit/JitcodeMap.h" #include "jit/JitSpewer.h" #include "js/TrackedOptimizationInfo.h" +#include "vm/UnboxedObject.h" #include "vm/ObjectGroup-inl.h" #include "vm/TypeInference-inl.h" +#include "vm/UnboxedObject-inl.h" using namespace js; using namespace js::jit; diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index 767cff661a..2475dfb228 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" diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 0fed05835f..3b4c957dc8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -45,6 +45,7 @@ #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; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index b17c845bb7..7852b33659 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -54,6 +54,7 @@ #include "vm/RegExpStaticsObject.h" #include "vm/Shape.h" #include "vm/TypedArrayCommon.h" +#include "vm/UnboxedObject-inl.h" #include "jsatominlines.h" #include "jsboolinlines.h" diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 5f476c4ffc..710f1d89b1 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 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index e6d6630c4b..3cf2deb834 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -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,15 +4927,10 @@ 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); From cc936df2e5b4913a0ecf1fe19a4bbd773e4d114d Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 16 May 2019 19:54:19 +0000 Subject: [PATCH 12/18] Remove unboxed object code from jit, Part 1 --- js/src/jit/AliasAnalysisShared.cpp | 2 - js/src/jit/CodeGenerator.cpp | 61 --------- js/src/jit/CodeGenerator.h | 3 - js/src/jit/IonAnalysis.cpp | 2 - js/src/jit/IonBuilder.cpp | 192 +--------------------------- js/src/jit/IonBuilder.h | 6 - js/src/jit/Lowering.cpp | 26 ---- js/src/jit/Lowering.h | 3 - js/src/jit/MIR.cpp | 159 +++-------------------- js/src/jit/MIR.h | 131 ------------------- js/src/jit/MOpcodes.h | 3 - js/src/jit/shared/LIR-shared.h | 48 ------- js/src/jit/shared/LOpcodes-shared.h | 3 - js/src/vm/TypeInference.cpp | 11 -- js/src/vm/TypeInference.h | 1 - 15 files changed, 25 insertions(+), 626 deletions(-) diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index 81c0fd0672..3b83df74e1 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -114,8 +114,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/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 16d0260929..6d1fb6b9b3 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3031,15 +3031,6 @@ GuardReceiver(MacroAssembler& masm, const ReceiverGuard& guard, { 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); } @@ -3077,13 +3068,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 +3108,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 @@ -3161,13 +3143,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) { @@ -3332,27 +3307,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) { @@ -8576,21 +8530,6 @@ 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"); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index b226f6cc91..292f163b5f 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); @@ -310,7 +308,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 d255c32a82..41c71c9c3c 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 28fa53aa8d..af85011be2 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; @@ -6392,7 +6391,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 +6428,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); @@ -7718,8 +7717,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,8 +8175,7 @@ IonBuilder::maybeMarkEmpty(MDefinition* ins) static bool ClassHasEffectlessLookup(const Class* clasp) { - return (clasp == &UnboxedPlainObject::class_) || - (clasp == &UnboxedArrayObject::class_) || + return (clasp == &UnboxedArrayObject::class_) || IsTypedObjectClass(clasp) || (clasp->isNative() && !clasp->getOpsLookupProperty()); } @@ -10970,11 +10966,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) { @@ -11031,8 +11024,6 @@ IonBuilder::getUnboxedOffset(TemporaryTypeSet* types, PropertyName* name, JSValu return UINT32_MAX; } - key->watchStateChangeForUnboxedConvertedToNative(constraints()); - if (offset == UINT32_MAX) { offset = property->offset; *punboxedType = property->type; @@ -11565,11 +11556,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) @@ -12085,34 +12071,6 @@ IonBuilder::loadUnboxedValue(MDefinition* elements, size_t elementsOffset, 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, @@ -12356,45 +12314,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; } @@ -12669,13 +12588,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)) @@ -13040,40 +12952,6 @@ IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t e 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, @@ -13112,46 +12990,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; } @@ -14178,19 +14016,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) @@ -14200,15 +14025,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); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 2880c7ea1d..77528ad372 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -401,7 +401,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 +440,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 +472,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); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 709de99873..aafa57c098 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3251,14 +3251,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 +3768,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 9b4095aec0..e36620bce3 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -232,7 +232,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 +255,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/MIR.cpp b/js/src/jit/MIR.cpp index 287b875824..1f33b21740 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) @@ -5945,8 +5858,6 @@ jit::UnboxedArrayElementType(CompilerConstraintList* constraints, MDefinition* o elementType = layout.elementType(); else return JSVAL_TYPE_MAGIC; - - key->watchStateChangeForUnboxedConvertedToNative(constraints); } return elementType; @@ -6579,23 +6490,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 +6520,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 fb0f22fc35..3e04217892 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 @@ -9742,59 +9741,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, @@ -11174,11 +11120,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 +11214,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 +11322,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, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index fddc1e637d..b80d1baf97 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) \ @@ -219,7 +217,6 @@ namespace jit { _(StoreUnboxedScalar) \ _(StoreUnboxedObjectOrNull) \ _(StoreUnboxedString) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShift) \ _(ArrayPush) \ _(ArraySlice) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index f4adcc63c0..f386d52569 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -5891,22 +5891,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 +7413,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 fe2ab5ea35..6912e8904a 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) \ @@ -287,7 +285,6 @@ _(StoreElementT) \ _(StoreUnboxedScalar) \ _(StoreUnboxedPointer) \ - _(ConvertUnboxedObjectToNative) \ _(ArrayPopShiftV) \ _(ArrayPopShiftT) \ _(ArrayPushV) \ diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index fe89fac9ce..9e03423828 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -1995,17 +1995,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) { diff --git a/js/src/vm/TypeInference.h b/js/src/vm/TypeInference.h index 0f1cd49362..04fed448c6 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); From 2db998a4f8d7b0a2d23f61068bc1817a79780f43 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Thu, 16 May 2019 21:34:42 +0000 Subject: [PATCH 13/18] Remove Unboxed Objects from vm/ - Part 2 --- js/src/jsobj.cpp | 75 +++++++++---------------------------- js/src/vm/ReceiverGuard.cpp | 1 - js/src/vm/ReceiverGuard.h | 5 --- 3 files changed, 17 insertions(+), 64 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 7852b33659..f22ecb4452 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -870,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 @@ -1167,46 +1164,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 @@ -1228,8 +1206,9 @@ 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() || + obj->is()); MOZ_ASSERT(newKind != SingletonObject); if (obj->is() || obj->is()) { @@ -1348,7 +1327,6 @@ 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; @@ -2334,11 +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); @@ -2591,11 +2564,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; @@ -2619,9 +2587,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)) @@ -3715,12 +3680,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(); diff --git a/js/src/vm/ReceiverGuard.cpp b/js/src/vm/ReceiverGuard.cpp index 97df908c3b..11c2d07278 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; diff --git a/js/src/vm/ReceiverGuard.h b/js/src/vm/ReceiverGuard.h index 459cc0012d..c14f0d83b5 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; From 28fbea676a2205c0fe795a5815707a2c9bf82340 Mon Sep 17 00:00:00 2001 From: Mihai Moldovan Date: Wed, 22 May 2019 21:10:26 +0200 Subject: [PATCH 14/18] Enable double buffering when using XRENDER. This fixes flickering/bars/stripes showing up during quickly-updating operations on Intel hardware when using XRENDER. For more information, refer to the code comment. See #1061 --- gfx/layers/client/ContentClient.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp index 3373230a99..50e159a236 100644 --- a/gfx/layers/client/ContentClient.cpp +++ b/gfx/layers/client/ContentClient.cpp @@ -78,8 +78,21 @@ ContentClient::CreateContentClient(CompositableForwarder* aForwarder) // We can't use double buffering when using image content with // Xrender support on Linux, as ContentHostDoubleBuffered is not // suited for direct uploads to the server. + // FIXME: Even though the comment above suggests that double buffering + // is supposed to be disabled when Xrender support is being enabled + // (and used), it really wasn't. Historically, + // UseImageOffscreenSurfaces() was always false in GTK2 builds, thus + // triggering the check, regardless of UseXRender(). + // Some time later, offscreen surfaces were always enabled, but the + // Xrender functionality broke due to not using Xlib-based surfaces. + // Using Xlib-based surfaces compatible with Xrender operations seems + // to lead to weird graphical artifacts (bars and stripes) on some + // hardware (Intel-based?) when displaying quickly-changing content, + // so contrary to the statement above we'd better enable double + // buffering - which also seems to not have any negative performance + // impact. if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() || - !gfxVars::UseXRender()) + gfxVars::UseXRender()) #endif { useDoubleBuffering = (LayerManagerComposite::SupportsDirectTexturing() && From 838eb9a504ead7b8e667ca32a6a916481d36d7e4 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Thu, 23 May 2019 19:47:59 -0400 Subject: [PATCH 15/18] Issue #1101 - Support gzip-compressed SVGs in OpenType+SVG fonts --- gfx/thebes/gfxSVGGlyphs.cpp | 38 ++++++++++++++++++ layout/reftests/text-svgglyphs/reftest.list | 1 + .../text-svgglyphs/resources/svg-gz.ttf | Bin 0 -> 113804 bytes .../svg-glyph-compressed-ref.html | 15 +++++++ .../text-svgglyphs/svg-glyph-compressed.html | 15 +++++++ 5 files changed, 69 insertions(+) create mode 100644 layout/reftests/text-svgglyphs/resources/svg-gz.ttf create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-compressed-ref.html create mode 100644 layout/reftests/text-svgglyphs/svg-glyph-compressed.html diff --git a/gfx/thebes/gfxSVGGlyphs.cpp b/gfx/thebes/gfxSVGGlyphs.cpp index a7615eca82..23f68f590c 100644 --- a/gfx/thebes/gfxSVGGlyphs.cpp +++ b/gfx/thebes/gfxSVGGlyphs.cpp @@ -31,6 +31,7 @@ #include "nsSMILAnimationController.h" #include "gfxContext.h" #include "harfbuzz/hb.h" +#include "zlib.h" #include "mozilla/dom/ImageTracker.h" #define SVG_CONTENT_TYPE NS_LITERAL_CSTRING("image/svg+xml") @@ -285,7 +286,44 @@ gfxSVGGlyphsDocument::gfxSVGGlyphsDocument(const uint8_t *aBuffer, gfxSVGGlyphs *aSVGGlyphs) : mOwner(aSVGGlyphs) { + if (aBufLen >= 14 && aBuffer[0] == 31 && aBuffer[1] == 139) { + // It's a gzip-compressed document; decompress it before parsing. + // The original length (modulo 2^32) is found in the last 4 bytes + // of the data, stored in little-endian format. We read it as + // individual bytes to avoid possible alignment issues. + // (Note that if the original length was >2^32, then origLen here + // will be incorrect; but then the inflate() call will not return + // Z_STREAM_END and we'll bail out safely.) + size_t origLen = (size_t(aBuffer[aBufLen - 1]) << 24) + + (size_t(aBuffer[aBufLen - 2]) << 16) + + (size_t(aBuffer[aBufLen - 3]) << 8) + + size_t(aBuffer[aBufLen - 4]); + AutoTArray outBuf; + if (outBuf.SetLength(origLen, mozilla::fallible)) { + z_stream s = {0}; + s.next_in = const_cast(aBuffer); + s.avail_in = aBufLen; + s.next_out = outBuf.Elements(); + s.avail_out = outBuf.Length(); + // The magic number 16 here is the zlib flag to expect gzip format, + // see http://www.zlib.net/manual.html#Advanced + if (Z_OK == inflateInit2(&s, 16 + MAX_WBITS)) { + int result = inflate(&s, Z_FINISH); + if (Z_STREAM_END == result) { + MOZ_ASSERT(size_t(s.next_out - outBuf.Elements()) == origLen); + ParseDocument(outBuf.Elements(), outBuf.Length()); + } else { + NS_WARNING("Failed to decompress SVG glyphs document"); + } + inflateEnd(&s); + } + } else { + NS_WARNING("Failed to allocate memory for SVG glyphs document"); + } + } else { ParseDocument(aBuffer, aBufLen); + } + if (!mDocument) { NS_WARNING("Could not parse SVG glyphs document"); return; diff --git a/layout/reftests/text-svgglyphs/reftest.list b/layout/reftests/text-svgglyphs/reftest.list index 6f328ab739..7ff341f429 100644 --- a/layout/reftests/text-svgglyphs/reftest.list +++ b/layout/reftests/text-svgglyphs/reftest.list @@ -21,3 +21,4 @@ pref(gfx.font_rendering.opentype_svg.enabled,true) fails == svg-glyph-mask.sv pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-paint-server.svg svg-glyph-paint-server-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-transform.svg svg-glyph-transform-ref.svg pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-extents.html svg-glyph-extents-ref.html +pref(gfx.font_rendering.opentype_svg.enabled,true) == svg-glyph-compressed.html svg-glyph-compressed-ref.html diff --git a/layout/reftests/text-svgglyphs/resources/svg-gz.ttf b/layout/reftests/text-svgglyphs/resources/svg-gz.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3a9660224ad20b70633e64a4adfc6425a3da7b95 GIT binary patch literal 113804 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

!b~;`b!p(7U9q|vv%@g896UJ0A+W#dMrpTAo znH!jSF=T7DhnTB)TH+~nmU)CTCtk4oDKAZ$TEEG6ly9c2H19%sCfGbsOt`-)D}Aw3 zWzx+YLz&5F@4U9GB#zKU47KIt7PdRcF9n#R*u-5+($vA#7qzhW&_KnJ7ACAb$2_7Q zDTg&I_ETQPm}69F{Fy~F@h#CnXT+a5@wyE+=dj;|*>CGG`NX_)Io`vh74K=D)zl4otVpfrv5#ZyyBDGb zbOHZOTvlP55?@2P5AlUYrJKscF-zHJTpiqau3_qyjMzvXy~F+66K0(G zUTgDzE&E#FH}7t?$@P-lo|LEQ2`Opn6xD`(v7$7Zoo&$CY=iwXfmUQ2(zFeh0)h~6 z+b|~G+`pJDOSm_7>5ADpDycrZVe+>Qp`MK9Sjrn)UYK&R3IA8~zG)c!p7O0zpPD+& zmLujpBz5OH5r&4To7$Nam-c;H^WHw}H}Pq<6M3#&xMlqB%KwxymHcJv$dqky{-d{P z;?%sCYRB6L~$K1c_M zAhQ*Tq6n^-adI_v=W4d!;2u$T8aF{y*IBUIG>fvd9{U?{eE-C{gy&0krk*vf7%mc* z3s*bY)neN>*_q?Zr)axe9 z9Mjr{eHjgdIJbra(4!ODDz zKXHB`8Iri1@LXnJvdd>=4!8cIopVaGbNg2G#~;acBW-<>4q1kK*#F7*A&JPfXm|+q z&^d}Vz^atp_0WIf=d2KesoSb?Z6mKWa*10L-~Vp>nlv%Tr=-h&e6G>x;~8Gb#pYd+ zUxK)124tu#$h_&1d2{gX{G-h48aF1c{wn7g887LWBsmV5$zGp)%hu*elNU{%P$o@W z(nERt^K?+eHF-Y=E)QwdfNe8eL0m;#5nKc=Kdu-K3)vzbmj@S%tBW(on{8b3)*R=H zGi6}P{-XHJah;R5N#9!xddE1@&)hc)t{g5Et|pG-6Mr}P2fuOV`YHD^*Tv5HKRkC3 zVfAsbxNw~7b64X3{}Inky&a-8->s<^eE4=uJ>W%NX+i#diaW^vO8oDSKMH5+*BG|P z2tPsi`)miY4Px7eZ7lm9P{$o7&buK8WuPp(EizM|y2*#Q3nB+&53<^Onfv|K{gU|H z+~3~Y+&{%dAv;=^cuz(l=eI$IO}V$ZzpHzX|K{E<&fJ^wH^FXQ?`huE)tT~}Yrnd0 z%Kh2ecrbBcTy0!`!bZRrQHl3?Hea@hY_s7S;!Hh2e8C&US8|+HG4b}wok5SS z#2fEui=NzJGgdyIVs%`^4m ze;>XXllJK;M>!Yg&nS+hXD-No^Stytb6nE%nKU)-cb<*sf-4iypN+Kf+KDpKyra1u z*H8Jz+1J42Khn)zrnHfmb`jbbaIrYU`?*b>XxZIxyJ2{^n{x`{${_Po_q{-peuF8yEB+ja*4 literal 0 HcmV?d00001 diff --git a/layout/reftests/text-svgglyphs/svg-glyph-compressed-ref.html b/layout/reftests/text-svgglyphs/svg-glyph-compressed-ref.html new file mode 100644 index 0000000000..9bbfeb7483 --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-compressed-ref.html @@ -0,0 +1,15 @@ + + +Test for compressed SVG glyphs + +

abcdefg
+
LMNOPQR
+ \ No newline at end of file diff --git a/layout/reftests/text-svgglyphs/svg-glyph-compressed.html b/layout/reftests/text-svgglyphs/svg-glyph-compressed.html new file mode 100644 index 0000000000..e8123bba69 --- /dev/null +++ b/layout/reftests/text-svgglyphs/svg-glyph-compressed.html @@ -0,0 +1,15 @@ + + +Test for compressed SVG glyphs + +
abcdefg
+
LMNOPQR
+ \ No newline at end of file From 70c0d40d9baa704bdbf5ea8428f8bdc8972056ee Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Thu, 23 May 2019 13:29:20 +0800 Subject: [PATCH 16/18] Issue #1104 - Set the browser's opener when adding a new tab - This modifies `loadOneTab` and `addTab` to accept an opener - This code was adapted from Basilisk's copy of tabbrowser.xml without the refactored code changes (which is a lot more involved as it divides addTab's functions into multiple functions) --- application/palemoon/base/content/tabbrowser.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/application/palemoon/base/content/tabbrowser.xml b/application/palemoon/base/content/tabbrowser.xml index aa1a89200e..b5edd54b7d 100644 --- a/application/palemoon/base/content/tabbrowser.xml +++ b/application/palemoon/base/content/tabbrowser.xml @@ -1337,6 +1337,7 @@ var aFromExternal; var aRelatedToCurrent; var aOriginPrincipal; + var aOpener; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1351,6 +1352,7 @@ aFromExternal = params.fromExternal; aRelatedToCurrent = params.relatedToCurrent; aOriginPrincipal = params.originPrincipal; + aOpener = params.opener; } var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : @@ -1366,7 +1368,8 @@ allowThirdPartyFixup: aAllowThirdPartyFixup, fromExternal: aFromExternal, originPrincipal: aOriginPrincipal, - relatedToCurrent: aRelatedToCurrent}); + relatedToCurrent: aRelatedToCurrent, + opener: aOpener }); if (!bgLoad) this.selectedTab = tab; @@ -1489,6 +1492,7 @@ var aSkipAnimation; var aOriginPrincipal; var aSkipBackgroundNotify; + var aOpener; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1504,6 +1508,7 @@ aRelatedToCurrent = params.relatedToCurrent; aSkipAnimation = params.skipAnimation; aOriginPrincipal = params.originPrincipal; + aOpener = params.opener; aSkipBackgroundNotify = params.skipBackgroundNotify; } @@ -1578,6 +1583,10 @@ b.setAttribute("showresizer", "true"); } + if (aOpener) { + b.QueryInterface(Ci.nsIFrameLoaderOwner).presetOpenerWindow(aOpener); + } + if (this.hasAttribute("autocompletepopup")) b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup")); b.setAttribute("autoscrollpopup", this._autoScrollPopup.id); From 18f9f38e8bd54c91bd24e6b429818378545aca88 Mon Sep 17 00:00:00 2001 From: FranklinDM Date: Thu, 23 May 2019 13:39:01 +0800 Subject: [PATCH 17/18] Issue #1104 - Pass an opener to loadOneTab in the openURI function --- application/palemoon/base/content/browser.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/palemoon/base/content/browser.js b/application/palemoon/base/content/browser.js index cc362d23f9..16b7c00199 100644 --- a/application/palemoon/base/content/browser.js +++ b/application/palemoon/base/content/browser.js @@ -4481,13 +4481,15 @@ nsBrowserAccess.prototype = { } let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"); + let openerWindow = (aContext & Ci.nsIBrowserDOMWindow.OPEN_NO_OPENER) ? null : aOpener; let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", { triggeringPrincipal: triggeringPrincipal, referrerURI: referrer, referrerPolicy: referrerPolicy, fromExternal: isExternal, - inBackground: loadInBackground}); + inBackground: loadInBackground, + opener: openerWindow }); let browser = win.gBrowser.getBrowserForTab(tab); if (gPrefService.getBoolPref("browser.tabs.noWindowActivationOnExternal")) { From 9843f04b26cbbd7a7ec3bc0116e43d86b9205210 Mon Sep 17 00:00:00 2001 From: adeshkp Date: Wed, 22 May 2019 13:34:36 -0400 Subject: [PATCH 18/18] Remove a stubbed telemetry function from app AUS. --- toolkit/mozapps/update/nsUpdateService.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index a68d815aad..64eb995a2e 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -948,9 +948,6 @@ function handleFallbackToCompleteUpdate(update, postStaging) { update.setProperty("patchingFailed", oldType); } -function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) { -} - /** * Update Patch * @param patch @@ -1538,7 +1535,6 @@ UpdateService.prototype = { getService(Ci.nsIUpdateManager); var update = um.activeUpdate; var status = readStatusFile(getUpdatesDir()); - pingStateAndStatusCodes(update, true, status); // STATE_NONE status typically means that the update.status file is present // but a background download error occurred. if (status == STATE_NONE) { @@ -2146,8 +2142,6 @@ UpdateService.prototype = { if (!osApplyToDir) { LOG("UpdateService:applyOsUpdate - Error: osApplyToDir is not defined" + "in the nsIUpdate!"); - pingStateAndStatusCodes(aUpdate, false, - STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR); handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR); return; } @@ -2157,8 +2151,6 @@ UpdateService.prototype = { if (!updateFile.exists()) { LOG("UpdateService:applyOsUpdate - Error: OS update is not found at " + updateFile.path); - pingStateAndStatusCodes(aUpdate, false, - STATE_FAILED + ": " + FOTA_FILE_OPERATION_ERROR); handleUpdateFailure(aUpdate, FOTA_FILE_OPERATION_ERROR); return; } @@ -2173,8 +2165,6 @@ UpdateService.prototype = { } catch (e) { LOG("UpdateService:applyOsUpdate - Error: Couldn't reboot into recovery" + " to apply FOTA update " + updateFile.path); - pingStateAndStatusCodes(aUpdate, false, - STATE_FAILED + ": " + FOTA_RECOVERY_ERROR); writeStatusFile(getUpdatesDir(), aUpdate.state = STATE_APPLIED); handleUpdateFailure(aUpdate, FOTA_RECOVERY_ERROR); } @@ -2476,7 +2466,6 @@ UpdateManager.prototype = { return; } var status = readStatusFile(getUpdatesDir()); - pingStateAndStatusCodes(update, false, status); var parts = status.split(":"); update.state = parts[0]; if (update.state == STATE_FAILED && parts[1]) {