Bug 1320670 part 2 - Use GetPropIRGenerator for GETELEM stubs in Baseline. r=h4writer
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 05 Dec 2016 15:44:35 -1000
changeset 325266 17f5bc695acc81a98f34c95eef31454c1706aa56
parent 325265 6b75807ac2e04eb448ed0f6c1b27b526e17a0669
child 325267 93d785448a69cd60c7105bfe722cfd94b6d59b37
push id84640
push userjandemooij@gmail.com
push dateTue, 06 Dec 2016 01:52:01 +0000
treeherdermozilla-inbound@17f5bc695acc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1320670
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1320670 part 2 - Use GetPropIRGenerator for GETELEM stubs in Baseline. r=h4writer
js/src/jit/BaselineCacheIR.cpp
js/src/jit/BaselineDebugModeOSR.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineICList.h
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/SharedIC.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -929,16 +929,27 @@ BaselineCacheIRCompiler::emitGuardIsStri
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestString(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardIsSymbol()
+{
+    ValueOperand input = allocator.useValueRegister(masm, reader.valOperandId());
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+    masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitGuardType()
 {
     ValueOperand input = allocator.useValueRegister(masm, reader.valOperandId());
     JSValueType type = reader.valueType();
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
@@ -1087,16 +1098,77 @@ BaselineCacheIRCompiler::emitGuardSpecif
         return false;
 
     Address addr(stubAddress(reader.stubOffset()));
     masm.branchPtr(Assembler::NotEqual, addr, obj, failure->label());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardSpecificAtom()
+{
+    Register str = allocator.useRegister(masm, reader.stringOperandId());
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Address atomAddr(stubAddress(reader.stubOffset()));
+
+    Label done;
+    masm.branchPtr(Assembler::Equal, atomAddr, str, &done);
+
+    // The pointers are not equal, so if the input string is also an atom it
+    // must be a different string.
+    masm.branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
+                      Imm32(JSString::ATOM_BIT), failure->label());
+
+    // Check the length.
+    masm.loadPtr(atomAddr, scratch);
+    masm.loadStringLength(scratch, scratch);
+    masm.branch32(Assembler::NotEqual, Address(str, JSString::offsetOfLength()),
+                  scratch, failure->label());
+
+    // We have a non-atomized string with the same length. Call a helper
+    // function to do the comparison.
+    LiveRegisterSet volatileRegs(RegisterSet::Volatile());
+    masm.PushRegsInMask(volatileRegs);
+
+    masm.setupUnalignedABICall(scratch);
+    masm.loadPtr(atomAddr, scratch);
+    masm.passABIArg(scratch);
+    masm.passABIArg(str);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, EqualStringsHelper));
+    masm.mov(ReturnReg, scratch);
+
+    LiveRegisterSet ignore;
+    ignore.add(scratch);
+    masm.PopRegsInMaskIgnore(volatileRegs, ignore);
+    masm.branchIfFalseBool(scratch, failure->label());
+
+    masm.bind(&done);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitGuardSpecificSymbol()
+{
+    Register sym = allocator.useRegister(masm, reader.symbolOperandId());
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    Address addr(stubAddress(reader.stubOffset()));
+    masm.branchPtr(Assembler::NotEqual, addr, sym, failure->label());
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitGuardMagicValue()
 {
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
     JSWhyMagic magic = reader.whyMagic();
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
@@ -1281,16 +1353,44 @@ BaselineCacheIRCompiler::emitCallProxyGe
 
     if (!callVM(masm, ProxyGetPropertyInfo))
         return false;
 
     stubFrame.leave(masm);
     return true;
 }
 
+typedef bool (*ProxyGetPropertyByValueFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
+static const VMFunction ProxyGetPropertyByValueInfo =
+    FunctionInfo<ProxyGetPropertyByValueFn>(ProxyGetPropertyByValue, "ProxyGetPropertyByValue");
+
+bool
+BaselineCacheIRCompiler::emitCallProxyGetByValueResult()
+{
+    AutoStubFrame stubFrame(*this);
+
+    Register obj = allocator.useRegister(masm, reader.objOperandId());
+    ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
+
+    AutoScratchRegister scratch(allocator, masm);
+
+    allocator.discardStack(masm);
+
+    stubFrame.enter(masm, scratch);
+
+    masm.Push(idVal);
+    masm.Push(obj);
+
+    if (!callVM(masm, ProxyGetPropertyByValueInfo))
+        return false;
+
+    stubFrame.leave(masm);
+    return true;
+}
+
 bool
 BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
 
     JSValueType fieldType = reader.valueType();
     Address fieldOffset(stubAddress(reader.stubOffset()));
@@ -1572,18 +1672,21 @@ BaselineCacheIRCompiler::emitGuardDOMExp
 
 bool
 BaselineCacheIRCompiler::init(CacheKind kind)
 {
     size_t numInputs = writer_.numInputOperands();
     if (!allocator.init(ICStubCompiler::availableGeneralRegs(numInputs)))
         return false;
 
-    MOZ_ASSERT(numInputs == 1);
-    allocator.initInputLocation(0, R0);
+    if (numInputs >= 1) {
+        allocator.initInputLocation(0, R0);
+        if (numInputs >= 2)
+            allocator.initInputLocation(1, R1);
+    }
 
     return true;
 }
 
 template <typename T>
 static GCPtr<T>*
 AsGCPtr(uintptr_t* ptr)
 {
@@ -1626,16 +1729,22 @@ CacheIRWriter::copyStubData(uint8_t* des
             InitGCPtr<Shape*>(destWords, stubFields_[i].asWord());
             break;
           case StubField::Type::JSObject:
             InitGCPtr<JSObject*>(destWords, stubFields_[i].asWord());
             break;
           case StubField::Type::ObjectGroup:
             InitGCPtr<ObjectGroup*>(destWords, stubFields_[i].asWord());
             break;
+          case StubField::Type::Symbol:
+            InitGCPtr<JS::Symbol*>(destWords, stubFields_[i].asWord());
+            break;
+          case StubField::Type::String:
+            InitGCPtr<JSString*>(destWords, stubFields_[i].asWord());
+            break;
           case StubField::Type::Id:
             InitGCPtr<jsid>(destWords, stubFields_[i].asWord());
             break;
           case StubField::Type::RawInt64:
             *reinterpret_cast<uint64_t*>(destWords) = stubFields_[i].asInt64();
             break;
           case StubField::Type::Value:
             InitGCPtr<JS::Value>(destWords, stubFields_[i].asInt64());
@@ -1720,17 +1829,17 @@ jit::AttachBaselineCacheIRStub(JSContext
 
     if (writer.failed())
         return nullptr;
 
     // Just a sanity check: the caller should ensure we don't attach an
     // unlimited number of stubs.
     MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs);
 
-    MOZ_ASSERT(kind == CacheKind::GetProp);
+    MOZ_ASSERT(kind == CacheKind::GetProp || kind == CacheKind::GetElem);
     uint32_t stubDataOffset = sizeof(ICCacheIR_Monitored);
 
     JitCompartment* jitCompartment = cx->compartment()->jitCompartment();
 
     // Check if we already have JitCode for this stub.
     CacheIRStubInfo* stubInfo;
     CacheIRStubKey::Lookup lookup(kind, engine, writer.codeStart(), writer.codeLength());
     JitCode* code = jitCompartment->getCacheIRStubCode(lookup, &stubInfo);
@@ -1800,16 +1909,24 @@ jit::TraceBaselineCacheIRStub(JSTracer* 
           case StubField::Type::ObjectGroup:
             TraceNullableEdge(trc, &stubInfo->getStubField<ObjectGroup*>(stub, offset),
                               "baseline-cacheir-group");
             break;
           case StubField::Type::JSObject:
             TraceNullableEdge(trc, &stubInfo->getStubField<JSObject*>(stub, offset),
                               "baseline-cacheir-object");
             break;
+          case StubField::Type::Symbol:
+            TraceNullableEdge(trc, &stubInfo->getStubField<JS::Symbol*>(stub, offset),
+                              "baseline-cacheir-symbol");
+            break;
+          case StubField::Type::String:
+            TraceNullableEdge(trc, &stubInfo->getStubField<JSString*>(stub, offset),
+                              "baseline-cacheir-string");
+            break;
           case StubField::Type::Id:
             TraceEdge(trc, &stubInfo->getStubField<jsid>(stub, offset), "baseline-cacheir-id");
             break;
           case StubField::Type::Value:
             TraceEdge(trc, &stubInfo->getStubField<JS::Value>(stub, offset),
                       "baseline-cacheir-value");
             break;
           case StubField::Type::Limit:
@@ -1856,16 +1973,22 @@ CacheIRStubInfo::copyStubData(ICStub* sr
             getStubField<Shape*>(dest, offset).init(getStubField<Shape*>(src, offset));
             break;
           case StubField::Type::JSObject:
             getStubField<JSObject*>(dest, offset).init(getStubField<JSObject*>(src, offset));
             break;
           case StubField::Type::ObjectGroup:
             getStubField<ObjectGroup*>(dest, offset).init(getStubField<ObjectGroup*>(src, offset));
             break;
+          case StubField::Type::Symbol:
+            getStubField<JS::Symbol*>(dest, offset).init(getStubField<JS::Symbol*>(src, offset));
+            break;
+          case StubField::Type::String:
+            getStubField<JSString*>(dest, offset).init(getStubField<JSString*>(src, offset));
+            break;
           case StubField::Type::Id:
             getStubField<jsid>(dest, offset).init(getStubField<jsid>(src, offset));
             break;
           case StubField::Type::Value:
             getStubField<Value>(dest, offset).init(getStubField<Value>(src, offset));
             break;
           case StubField::Type::Limit:
             return; // Done.
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -693,20 +693,16 @@ RecompileBaselineScriptForDebugMode(JSCo
     _(CacheIR_Monitored)                        \
     _(Call_Scripted)                            \
     _(Call_AnyScripted)                         \
     _(Call_Native)                              \
     _(Call_ClassHook)                           \
     _(Call_ScriptedApplyArray)                  \
     _(Call_ScriptedApplyArguments)              \
     _(Call_ScriptedFunCall)                     \
-    _(GetElem_NativePrototypeCallNativeName)    \
-    _(GetElem_NativePrototypeCallNativeSymbol)  \
-    _(GetElem_NativePrototypeCallScriptedName)  \
-    _(GetElem_NativePrototypeCallScriptedSymbol) \
     _(GetProp_CallNativeGlobal)                 \
     _(GetProp_Generic)                          \
     _(SetProp_CallScripted)                     \
     _(SetProp_CallNative)
 
 static bool
 CloneOldBaselineStub(JSContext* cx, DebugModeOSREntryVector& entries, size_t entryIndex)
 {
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/TemplateLib.h"
 
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
 #include "builtin/SIMD.h"
 #include "gc/Policy.h"
+#include "jit/BaselineCacheIR.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
@@ -847,189 +848,16 @@ IsCacheableSetPropCall(JSContext* cx, JS
         *isTemporarilyUnoptimizable = true;
         return false;
     }
 
     *isScripted = true;
     return true;
 }
 
-template <class T>
-static bool
-GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleObject obj, HandleObject holder,
-                        Handle<T> key, bool needsAtomize)
-{
-    bool indirect = (obj.get() != holder.get());
-    MOZ_ASSERT_IF(indirect, holder->isNative());
-
-    for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (iter->kind() != ICStub::GetElem_NativeSlotName &&
-            iter->kind() != ICStub::GetElem_NativeSlotSymbol &&
-            iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
-            iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol)
-        {
-            continue;
-        }
-
-        if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol))
-        {
-            continue;
-        }
-
-        if(mozilla::IsSame<T, JS::Symbol*>::value !=
-           static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
-        {
-            continue;
-        }
-
-        ICGetElemNativeStubImpl<T>* getElemNativeStub =
-            reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
-        if (key != getElemNativeStub->key())
-            continue;
-
-        if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
-            continue;
-
-        // If the new stub needs atomization, and the old stub doesn't atomize, then
-        // an appropriate stub doesn't exist.
-        if (needsAtomize && !getElemNativeStub->needsAtomize())
-            continue;
-
-        // For prototype gets, check the holder and holder shape.
-        if (indirect) {
-            if (iter->isGetElem_NativePrototypeSlotName() ||
-                iter->isGetElem_NativePrototypeSlotSymbol()) {
-                ICGetElem_NativePrototypeSlot<T>* protoStub =
-                    reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
-
-                if (holder != protoStub->holder())
-                    continue;
-
-                if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
-                    continue;
-            } else {
-                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
-                           iter->isGetElem_NativePrototypeCallNativeSymbol() ||
-                           iter->isGetElem_NativePrototypeCallScriptedName() ||
-                           iter->isGetElem_NativePrototypeCallScriptedSymbol());
-
-                ICGetElemNativePrototypeCallStub<T>* protoStub =
-                    reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
-
-                if (holder != protoStub->holder())
-                    continue;
-
-                if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
-                    continue;
-            }
-        }
-
-        return true;
-    }
-    return false;
-}
-
-template <class T>
-static void
-RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, HandleObject obj,
-                                 HandleObject holder, Handle<T> key, bool needsAtomize)
-{
-    bool indirect = (obj.get() != holder.get());
-    MOZ_ASSERT_IF(indirect, holder->isNative());
-
-    for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
-        switch (iter->kind()) {
-          case ICStub::GetElem_NativeSlotName:
-          case ICStub::GetElem_NativeSlotSymbol:
-            if (indirect)
-                continue;
-            MOZ_FALLTHROUGH;
-          case ICStub::GetElem_NativePrototypeSlotName:
-          case ICStub::GetElem_NativePrototypeSlotSymbol:
-          case ICStub::GetElem_NativePrototypeCallNativeName:
-          case ICStub::GetElem_NativePrototypeCallNativeSymbol:
-          case ICStub::GetElem_NativePrototypeCallScriptedName:
-          case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
-            break;
-          default:
-            continue;
-        }
-
-        if(mozilla::IsSame<T, JS::Symbol*>::value !=
-           static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
-        {
-            continue;
-        }
-
-        ICGetElemNativeStubImpl<T>* getElemNativeStub =
-            reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
-        if (key != getElemNativeStub->key())
-            continue;
-
-        if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
-            continue;
-
-        // For prototype gets, check the holder and holder shape.
-        if (indirect) {
-            if (iter->isGetElem_NativePrototypeSlotName() ||
-                iter->isGetElem_NativePrototypeSlotSymbol()) {
-                ICGetElem_NativePrototypeSlot<T>* protoStub =
-                    reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
-
-                if (holder != protoStub->holder())
-                    continue;
-
-                // If the holder matches, but the holder's lastProperty doesn't match, then
-                // this stub is invalid anyway.  Unlink it.
-                if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
-                    iter.unlink(cx);
-                    continue;
-                }
-            } else {
-                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
-                           iter->isGetElem_NativePrototypeCallNativeSymbol() ||
-                           iter->isGetElem_NativePrototypeCallScriptedName() ||
-                           iter->isGetElem_NativePrototypeCallScriptedSymbol());
-                ICGetElemNativePrototypeCallStub<T>* protoStub =
-                    reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
-
-                if (holder != protoStub->holder())
-                    continue;
-
-                // If the holder matches, but the holder's lastProperty doesn't match, then
-                // this stub is invalid anyway.  Unlink it.
-                if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
-                    iter.unlink(cx);
-                    continue;
-                }
-            }
-        }
-
-        // If the new stub needs atomization, and the old stub doesn't atomize, then
-        // remove the old stub.
-        if (needsAtomize && !getElemNativeStub->needsAtomize()) {
-            iter.unlink(cx);
-            continue;
-        }
-
-        // Should never get here, because this means a matching stub exists, and if
-        // a matching stub exists, this procedure should never have been called.
-        MOZ_CRASH("Procedure should never have been called.");
-    }
-}
-
 static bool
 TypedArrayGetElemStubExists(ICGetElem_Fallback* stub, HandleObject obj)
 {
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (!iter->isGetElem_TypedArray())
             continue;
         if (obj->maybeShape() == iter->toGetElem_TypedArray()->shape())
             return true;
@@ -1044,41 +872,16 @@ ArgumentsGetElemStubExists(ICGetElem_Fal
         if (!iter->isGetElem_Arguments())
             continue;
         if (iter->toGetElem_Arguments()->which() == which)
             return true;
     }
     return false;
 }
 
-template <class T>
-static T
-getKey(jsid id)
-{
-    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
-    return false;
-}
-
-template <>
-JS::Symbol* getKey<JS::Symbol*>(jsid id)
-{
-    if (!JSID_IS_SYMBOL(id))
-        return nullptr;
-    return JSID_TO_SYMBOL(id);
-}
-
-template <>
-PropertyName* getKey<PropertyName*>(jsid id)
-{
-    uint32_t dummy;
-    if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
-        return nullptr;
-    return JSID_TO_ATOM(id)->asPropertyName();
-}
-
 static bool
 IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp)
 {
     if (!key.isString())
         return false;
 
     // Convert to interned property name.
     if (!ValueToId<CanGC>(cx, key, idp))
@@ -1086,237 +889,16 @@ IsOptimizableElementPropertyName(JSConte
 
     uint32_t dummy;
     if (!JSID_IS_ATOM(idp) || JSID_TO_ATOM(idp)->isIndex(&dummy))
         return false;
 
     return true;
 }
 
-template <class T>
-static bool
-checkAtomize(HandleValue key)
-{
-    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
-    return false;
-}
-
-template <>
-bool checkAtomize<JS::Symbol*>(HandleValue key)
-{
-    return false;
-}
-
-template <>
-bool checkAtomize<PropertyName*>(HandleValue key)
-{
-    return !key.toString()->isAtom();
-}
-
-template <class T>
-static bool
-TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
-                                         ICGetElem_Fallback* stub, HandleObject obj,
-                                         HandleValue keyVal, bool* attached)
-{
-    MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
-
-    // Convert to id.
-    RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, keyVal, &id))
-        return false;
-
-    Rooted<T> key(cx, getKey<T>(id));
-    if (!key)
-        return true;
-    bool needsAtomize = checkAtomize<T>(keyVal);
-
-    RootedShape shape(cx);
-    RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
-        return false;
-    if (!holder || (holder != obj && !holder->isNative()))
-        return true;
-
-    // If a suitable stub already exists, nothing else to do.
-    if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
-        return true;
-
-    // Remove any existing stubs that may interfere with the new stub being added.
-    RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
-
-    ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-
-    if (obj->is<UnboxedPlainObject>() && holder == obj) {
-        const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
-
-        // Once unboxed objects support symbol-keys, we need to change the following accordingly
-        MOZ_ASSERT_IF(!keyVal.isString(), !property);
-
-        if (property) {
-            if (!cx->runtime()->jitSupportsFloatingPoint)
-                return true;
-
-            RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
-            ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName,
-                                                            monitorStub, obj, holder,
-                                                            name,
-                                                            ICGetElemNativeStub::UnboxedProperty,
-                                                            needsAtomize, property->offset +
-                                                            UnboxedPlainObject::offsetOfData(),
-                                                            property->type);
-            ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-            if (!newStub)
-                return false;
-
-            stub->addNewStub(newStub);
-            *attached = true;
-            return true;
-        }
-
-        Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id);
-        if (!shape->hasDefaultGetter() || !shape->hasSlot())
-            return true;
-
-        bool isFixedSlot;
-        uint32_t offset;
-        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-        ICGetElemNativeStub::AccessType acctype =
-            isFixedSlot ? ICGetElemNativeStub::FixedSlot
-                        : ICGetElemNativeStub::DynamicSlot;
-        ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
-                                            monitorStub, obj, holder, key,
-                                            acctype, needsAtomize, offset);
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
-    if (!holder->isNative())
-        return true;
-
-    if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
-        bool isFixedSlot;
-        uint32_t offset;
-        GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-        ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlotName
-                                            : ICStub::GetElem_NativePrototypeSlotName;
-        kind = getGetElemStubKind<T>(kind);
-
-        JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
-                                    "(obj=%p, holder=%p, holderShape=%p)",
-                    (obj == holder) ? "direct" : "prototype",
-                    needsAtomize ? " atomizing" : "",
-                obj.get(), holder.get(), holder->as<NativeObject>().lastProperty());
-
-        AccType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot
-                                      : ICGetElemNativeStub::DynamicSlot;
-        ICGetElemNativeCompiler<T> compiler(cx, kind, monitorStub, obj, holder, key,
-                                            acctype, needsAtomize, offset);
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
-    return true;
-}
-
-template <class T>
-static bool
-TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
-                                   ICGetElem_Fallback* stub, HandleNativeObject obj,
-                                   HandleValue keyVal, bool* attached,
-                                   bool* isTemporarilyUnoptimizable)
-{
-    MOZ_ASSERT(!*attached);
-    MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
-
-    RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, keyVal, &id))
-        return false;
-
-    Rooted<T> key(cx, getKey<T>(id));
-    if (!key)
-        return true;
-    bool needsAtomize = checkAtomize<T>(keyVal);
-
-    RootedShape shape(cx);
-    RootedObject baseHolder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, id, &baseHolder, &shape))
-        return false;
-    if (!baseHolder || !baseHolder->isNative())
-        return true;
-
-    HandleNativeObject holder = baseHolder.as<NativeObject>();
-
-    bool getterIsScripted = false;
-    if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted,
-                               isTemporarilyUnoptimizable, /*isDOMProxy=*/false))
-    {
-        RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
-
-        // For now, we do not handle own property getters
-        if (obj == holder)
-            return true;
-
-        // If a suitable stub already exists, nothing else to do.
-        if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
-            return true;
-
-        // Remove any existing stubs that may interfere with the new stub being added.
-        RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
-
-        ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-        ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScriptedName
-                                             : ICStub::GetElem_NativePrototypeCallNativeName;
-        kind = getGetElemStubKind<T>(kind);
-
-        if (getterIsScripted) {
-            JitSpew(JitSpew_BaselineIC,
-                    "  Generating GetElem(Native %s%s call scripted %s:%" PRIuSIZE ") stub "
-                    "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
-                        (obj == holder) ? "direct" : "prototype",
-                        needsAtomize ? " atomizing" : "",
-                        getter->nonLazyScript()->filename(), getter->nonLazyScript()->lineno(),
-                        obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
-        } else {
-            JitSpew(JitSpew_BaselineIC,
-                    "  Generating GetElem(Native %s%s call native) stub "
-                    "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
-                        (obj == holder) ? "direct" : "prototype",
-                        needsAtomize ? " atomizing" : "",
-                        obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
-        }
-
-        AccType acctype = getterIsScripted ? ICGetElemNativeStub::ScriptedGetter
-                                           : ICGetElemNativeStub::NativeGetter;
-        ICGetElemNativeCompiler<T> compiler(cx, kind, monitorStub, obj, holder, key, acctype,
-                                            needsAtomize, getter, script->pcToOffset(pc));
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!newStub)
-            return false;
-
-        stub->addNewStub(newStub);
-        *attached = true;
-        return true;
-    }
-
-    return true;
-}
-
 static bool
 IsPrimitiveArrayTypedObject(JSObject* obj)
 {
     if (!obj->is<TypedObject>())
         return false;
     TypeDescr& descr = obj->as<TypedObject>().typeDescr();
     return descr.is<ArrayTypeDescr>() &&
            descr.as<ArrayTypeDescr>().elementType().is<ScalarTypeDescr>();
@@ -1436,37 +1018,16 @@ TryAttachGetElemStub(JSContext* cx, JSSc
         if (!denseStub)
             return false;
 
         stub->addNewStub(denseStub);
         *attached = true;
         return true;
     }
 
-    // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
-    if (obj->isNative() || obj->is<UnboxedPlainObject>()) {
-        RootedScript rootedScript(cx, script);
-        if (rhs.isString()) {
-            if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
-                                                                         obj, rhs, attached))
-            {
-                return false;
-            }
-        } else if (rhs.isSymbol()) {
-            if (!TryAttachNativeOrUnboxedGetValueElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
-                                                                       obj, rhs, attached))
-            {
-                return false;
-            }
-        }
-        if (*attached)
-            return true;
-        script = rootedScript;
-    }
-
     // Check for UnboxedArray[int] accesses.
     if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) {
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(UnboxedArray[Int32]) stub");
         ICGetElem_UnboxedArray::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                                   obj->group());
         ICStub* unboxedStub = compiler.getStub(compiler.getStubSpace(script));
         if (!unboxedStub)
             return false;
@@ -1550,39 +1111,32 @@ DoGetElemFallback(JSContext* cx, Baselin
     bool attached = false;
     if (stub->numOptimizedStubs() >= ICGetElem_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         stub->noteUnoptimizableAccess();
         attached = true;
     }
 
-    // Try to attach an optimized getter stub.
     bool isTemporarilyUnoptimizable = false;
-    if (!attached && lhs.isObject() && lhs.toObject().isNative()){
-        if (rhs.isString()) {
-            RootedScript rootedScript(cx, frame->script());
-            RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
-            if (!TryAttachNativeGetAccessorElemStub<PropertyName*>(cx, rootedScript, pc, stub,
-                                                                   obj, rhs, &attached,
-                                                                   &isTemporarilyUnoptimizable))
-            {
-                return false;
+    if (!attached && !JitOptions.disableCacheIR) {
+        ICStubEngine engine = ICStubEngine::Baseline;
+        GetPropIRGenerator gen(cx, pc, engine, CacheKind::GetElem, &isTemporarilyUnoptimizable,
+                               lhs, rhs, res);
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        engine, info.outerScript(cx), stub);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                attached = true;
+                if (gen.shouldNotePreliminaryObjectStub())
+                    newStub->toCacheIR_Monitored()->notePreliminaryObject();
+                else if (gen.shouldUnlinkPreliminaryObjectStubs())
+                    StripPreliminaryObjectStubs(cx, stub);
             }
-            script = rootedScript;
-        } else if (rhs.isSymbol()) {
-            RootedScript rootedScript(cx, frame->script());
-            RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
-            if (!TryAttachNativeGetAccessorElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
-                                                                 obj, rhs, &attached,
-                                                                 &isTemporarilyUnoptimizable))
-            {
-                return false;
-            }
-            script = rootedScript;
         }
     }
 
     if (!isOptimizedArgs) {
         if (!GetElementOperation(cx, op, &lhsCopy, rhs, res))
             return false;
         TypeScript::Monitor(cx, frame->script(), pc, res);
     }
@@ -1634,362 +1188,16 @@ ICGetElem_Fallback::Compiler::generateSt
     masm.pushValue(R0);
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoGetElemFallbackInfo, masm);
 }
 
 //
-// GetElem_NativeSlot
-//
-
-static bool
-DoAtomizeString(JSContext* cx, HandleString string, MutableHandleValue result)
-{
-    JitSpew(JitSpew_BaselineIC, "  AtomizeString called");
-
-    RootedValue key(cx, StringValue(string));
-
-    // Convert to interned property name.
-    RootedId id(cx);
-    if (!ValueToId<CanGC>(cx, key, &id))
-        return false;
-
-    if (!JSID_IS_ATOM(id)) {
-        result.set(key);
-        return true;
-    }
-
-    result.set(StringValue(JSID_TO_ATOM(id)));
-    return true;
-}
-
-typedef bool (*DoAtomizeStringFn)(JSContext*, HandleString, MutableHandleValue);
-static const VMFunction DoAtomizeStringInfo = FunctionInfo<DoAtomizeStringFn>(DoAtomizeString,
-                                                                              "DoAtomizeString");
-
-template <class T>
-bool
-ICGetElemNativeCompiler<T>::emitCallNative(MacroAssembler& masm, Register objReg)
-{
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
-    regs.takeUnchecked(objReg);
-    regs.takeUnchecked(ICTailCallReg);
-
-    enterStubFrame(masm, regs.getAny());
-
-    // Push object.
-    masm.push(objReg);
-
-    // Push native callee.
-    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), objReg);
-    masm.push(objReg);
-
-    regs.add(objReg);
-
-    // Call helper.
-    if (!callVM(DoCallNativeGetterInfo, masm))
-        return false;
-
-    leaveStubFrame(masm);
-
-    return true;
-}
-
-template <class T>
-bool
-ICGetElemNativeCompiler<T>::emitCallScripted(MacroAssembler& masm, Register objReg)
-{
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
-    regs.takeUnchecked(objReg);
-    regs.takeUnchecked(ICTailCallReg);
-
-    // Enter stub frame.
-    enterStubFrame(masm, regs.getAny());
-
-    // Align the stack such that the JitFrameLayout is aligned on
-    // JitStackAlignment.
-    masm.alignJitStackBasedOnNArgs(0);
-
-    // Push |this| for getter (target object).
-    {
-        ValueOperand val = regs.takeAnyValue();
-        masm.tagValue(JSVAL_TYPE_OBJECT, objReg, val);
-        masm.Push(val);
-        regs.add(val);
-    }
-
-    regs.add(objReg);
-
-    Register callee = regs.takeAny();
-    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), callee);
-
-    // Push argc, callee, and descriptor.
-    {
-        Register callScratch = regs.takeAny();
-        EmitBaselineCreateStubFrameDescriptor(masm, callScratch, JitFrameLayout::Size());
-        masm.Push(Imm32(0));  // ActualArgc is 0
-        masm.Push(callee);
-        masm.Push(callScratch);
-        regs.add(callScratch);
-    }
-
-    Register code = regs.takeAnyExcluding(ArgumentsRectifierReg);
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
-    masm.loadBaselineOrIonRaw(code, code, nullptr);
-
-    Register scratch = regs.takeAny();
-
-    // Handle arguments underflow.
-    Label noUnderflow;
-    masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch);
-    masm.branch32(Assembler::Equal, scratch, Imm32(0), &noUnderflow);
-    {
-        // Call the arguments rectifier.
-        MOZ_ASSERT(ArgumentsRectifierReg != code);
-
-        JitCode* argumentsRectifier =
-            cx->runtime()->jitRuntime()->getArgumentsRectifier();
-
-        masm.movePtr(ImmGCPtr(argumentsRectifier), code);
-        masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
-        masm.movePtr(ImmWord(0), ArgumentsRectifierReg);
-    }
-
-    masm.bind(&noUnderflow);
-    masm.callJit(code);
-
-    leaveStubFrame(masm, true);
-
-    return true;
-}
-
-template <class T>
-bool
-ICGetElemNativeCompiler<T>::emitCheckKey(MacroAssembler& masm, Label& failure)
-{
-    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
-    return false;
-}
-
-template <>
-bool
-ICGetElemNativeCompiler<JS::Symbol*>::emitCheckKey(MacroAssembler& masm, Label& failure)
-{
-    MOZ_ASSERT(!needsAtomize_);
-    masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
-    Address symbolAddr(ICStubReg, ICGetElemNativeStubImpl<JS::Symbol*>::offsetOfKey());
-    Register symExtract = masm.extractObject(R1, ExtractTemp1);
-    masm.branchPtr(Assembler::NotEqual, symbolAddr, symExtract, &failure);
-    return true;
-}
-
-template <>
-bool
-ICGetElemNativeCompiler<PropertyName*>::emitCheckKey(MacroAssembler& masm, Label& failure)
-{
-    masm.branchTestString(Assembler::NotEqual, R1, &failure);
-    // Check key identity.  Don't automatically fail if this fails, since the incoming
-    // key maybe a non-interned string.  Switch to a slowpath vm-call based check.
-    Address nameAddr(ICStubReg, ICGetElemNativeStubImpl<PropertyName*>::offsetOfKey());
-    Register strExtract = masm.extractString(R1, ExtractTemp1);
-
-    // If needsAtomize_ is true, and the string is not already an atom, then atomize the
-    // string before proceeding.
-    if (needsAtomize_) {
-        Label skipAtomize;
-
-        // If string is already an atom, skip the atomize.
-        masm.branchTest32(Assembler::NonZero,
-                          Address(strExtract, JSString::offsetOfFlags()),
-                          Imm32(JSString::ATOM_BIT),
-                          &skipAtomize);
-
-        // Stow R0.
-        EmitStowICValues(masm, 1);
-
-        enterStubFrame(masm, R0.scratchReg());
-
-        // Atomize the string into a new value.
-        masm.push(strExtract);
-        if (!callVM(DoAtomizeStringInfo, masm))
-            return false;
-
-        // Atomized string is now in JSReturnOperand (R0).
-        // Leave stub frame, move atomized string into R1.
-        MOZ_ASSERT(R0 == JSReturnOperand);
-        leaveStubFrame(masm);
-        masm.moveValue(JSReturnOperand, R1);
-
-        // Unstow R0
-        EmitUnstowICValues(masm, 1);
-
-        // Extract string from R1 again.
-        DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1);
-        MOZ_ASSERT(Register(strExtract2) == strExtract);
-
-        masm.bind(&skipAtomize);
-    }
-
-    // Key has been atomized if necessary.  Do identity check on string pointer.
-    masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure);
-    return true;
-}
-
-template <class T>
-bool
-ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    Label failurePopR1;
-    bool popR1 = false;
-
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratchReg = regs.takeAny();
-
-    // Unbox object.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-
-    // Check object shape/group.
-    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratchReg,
-                        ICGetElemNativeStub::offsetOfReceiverGuard(), &failure);
-
-    // Since this stub sometimes enters a stub frame, we manually set this to true (lie).
-#ifdef DEBUG
-    entersStubFrame_ = true;
-#endif
-
-    if (!emitCheckKey(masm, failure))
-        return false;
-
-    Register holderReg;
-    if (obj_ == holder_) {
-        holderReg = objReg;
-
-        if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) {
-            // The property will be loaded off the unboxed expando.
-            masm.push(R1.scratchReg());
-            popR1 = true;
-            holderReg = R1.scratchReg();
-            masm.loadPtr(Address(objReg, UnboxedPlainObject::offsetOfExpando()), holderReg);
-        }
-    } else {
-        // Shape guard holder.
-        if (regs.empty()) {
-            masm.push(R1.scratchReg());
-            popR1 = true;
-            holderReg = R1.scratchReg();
-        } else {
-            holderReg = regs.takeAny();
-        }
-
-        if (kind == ICStub::GetElem_NativePrototypeCallNativeName ||
-            kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
-            kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
-            kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol)
-        {
-            masm.loadPtr(Address(ICStubReg,
-                                 ICGetElemNativePrototypeCallStub<T>::offsetOfHolder()),
-                         holderReg);
-            masm.loadPtr(Address(ICStubReg,
-                                 ICGetElemNativePrototypeCallStub<T>::offsetOfHolderShape()),
-                         scratchReg);
-        } else {
-            masm.loadPtr(Address(ICStubReg,
-                                 ICGetElem_NativePrototypeSlot<T>::offsetOfHolder()),
-                         holderReg);
-            masm.loadPtr(Address(ICStubReg,
-                                 ICGetElem_NativePrototypeSlot<T>::offsetOfHolderShape()),
-                         scratchReg);
-        }
-        masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratchReg,
-                                popR1 ? &failurePopR1 : &failure);
-    }
-
-    if (acctype_ == ICGetElemNativeStub::DynamicSlot ||
-        acctype_ == ICGetElemNativeStub::FixedSlot)
-    {
-        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
-                    scratchReg);
-
-        // Load from object.
-        if (acctype_ == ICGetElemNativeStub::DynamicSlot)
-            masm.addPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
-        else
-            masm.addPtr(holderReg, scratchReg);
-
-        Address valAddr(scratchReg, 0);
-        masm.loadValue(valAddr, R0);
-        if (popR1)
-            masm.addToStackPtr(ImmWord(sizeof(size_t)));
-
-    } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
-        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
-                    scratchReg);
-        masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
-                                 TypedOrValueRegister(R0));
-        if (popR1)
-            masm.addToStackPtr(ImmWord(sizeof(size_t)));
-    } else {
-        MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
-                   acctype_ == ICGetElemNativeStub::ScriptedGetter);
-        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
-                   kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
-                   kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
-                   kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
-
-        if (acctype_ == ICGetElemNativeStub::NativeGetter) {
-            // If calling a native getter, there is no chance of failure now.
-
-            // GetElem key (R1) is no longer needed.
-            if (popR1)
-                masm.addToStackPtr(ImmWord(sizeof(size_t)));
-
-            if (!emitCallNative(masm, objReg))
-                return false;
-
-        } else {
-            MOZ_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter);
-
-            // Load function in scratchReg and ensure that it has a jit script.
-            masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()),
-                         scratchReg);
-            masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure);
-            masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
-            masm.loadBaselineOrIonRaw(scratchReg, scratchReg, popR1 ? &failurePopR1 : &failure);
-
-            // At this point, we are guaranteed to successfully complete.
-            if (popR1)
-                masm.addToStackPtr(Imm32(sizeof(size_t)));
-
-            if (!emitCallScripted(masm, objReg))
-                return false;
-        }
-    }
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    if (popR1) {
-        masm.bind(&failurePopR1);
-        masm.pop(R1.scratchReg());
-    }
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    return true;
-}
-
-//
 // GetElem_String
 //
 
 bool
 ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
@@ -8174,111 +7382,16 @@ ICTypeUpdate_SingleObject::ICTypeUpdate_
     obj_(obj)
 { }
 
 ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGroup* group)
   : ICStub(TypeUpdate_ObjectGroup, stubCode),
     group_(group)
 { }
 
-ICGetElemNativeStub::ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode,
-                                         ICStub* firstMonitorStub,
-                                         ReceiverGuard guard, AccessType acctype,
-                                         bool needsAtomize, bool isSymbol)
-  : ICMonitoredStub(kind, stubCode, firstMonitorStub),
-    receiverGuard_(guard)
-{
-    extra_ = (static_cast<uint16_t>(acctype) << ACCESSTYPE_SHIFT) |
-             (static_cast<uint16_t>(needsAtomize) << NEEDS_ATOMIZE_SHIFT) |
-             (static_cast<uint16_t>(isSymbol) << ISSYMBOL_SHIFT);
-}
-
-ICGetElemNativeStub::~ICGetElemNativeStub()
-{ }
-
-template <class T>
-ICGetElemNativeGetterStub<T>::ICGetElemNativeGetterStub(
-                           ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                           ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
-                           JSFunction* getter, uint32_t pcOffset)
-  : ICGetElemNativeStubImpl<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize),
-    getter_(getter),
-    pcOffset_(pcOffset)
-{
-    MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
-               kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
-               kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
-               kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
-    MOZ_ASSERT(acctype == ICGetElemNativeStub::NativeGetter ||
-               acctype == ICGetElemNativeStub::ScriptedGetter);
-}
-
-template <class T>
-ICGetElem_NativePrototypeSlot<T>::ICGetElem_NativePrototypeSlot(
-                               JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
-                               const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
-                               JSObject* holder, Shape* holderShape)
-  : ICGetElemNativeSlotStub<T>(getGetElemStubKind<T>(ICStub::GetElem_NativePrototypeSlotName),
-                               stubCode, firstMonitorStub, guard, key, acctype, needsAtomize, offset),
-    holder_(holder),
-    holderShape_(holderShape)
-{ }
-
-template <class T>
-ICGetElemNativePrototypeCallStub<T>::ICGetElemNativePrototypeCallStub(
-                                  ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                  ReceiverGuard guard, const T* key, AccType acctype,
-                                  bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
-                                  JSObject* holder, Shape* holderShape)
-  : ICGetElemNativeGetterStub<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize,
-                                 getter, pcOffset),
-    holder_(holder),
-    holderShape_(holderShape)
-{}
-
-template <class T>
-/* static */ ICGetElem_NativePrototypeCallNative<T>*
-ICGetElem_NativePrototypeCallNative<T>::Clone(JSContext* cx,
-                                              ICStubSpace* space,
-                                              ICStub* firstMonitorStub,
-                                              ICGetElem_NativePrototypeCallNative<T>& other)
-{
-    return ICStub::New<ICGetElem_NativePrototypeCallNative<T>>(cx, space, other.jitCode(),
-                firstMonitorStub, other.receiverGuard(), &other.key().get(), other.accessType(),
-                other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
-                other.holderShape());
-}
-
-template ICGetElem_NativePrototypeCallNative<JS::Symbol*>*
-ICGetElem_NativePrototypeCallNative<JS::Symbol*>::Clone(JSContext*, ICStubSpace*, ICStub*,
-                                          ICGetElem_NativePrototypeCallNative<JS::Symbol*>&);
-template ICGetElem_NativePrototypeCallNative<js::PropertyName*>*
-ICGetElem_NativePrototypeCallNative<js::PropertyName*>::Clone(JSContext*, ICStubSpace*, ICStub*,
-                                          ICGetElem_NativePrototypeCallNative<js::PropertyName*>&);
-
-template <class T>
-/* static */ ICGetElem_NativePrototypeCallScripted<T>*
-ICGetElem_NativePrototypeCallScripted<T>::Clone(JSContext* cx,
-                                                ICStubSpace* space,
-                                                ICStub* firstMonitorStub,
-                                                ICGetElem_NativePrototypeCallScripted<T>& other)
-{
-    return ICStub::New<ICGetElem_NativePrototypeCallScripted<T>>(cx, space, other.jitCode(),
-                firstMonitorStub, other.receiverGuard(), &other.key().get(), other.accessType(),
-                other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
-                other.holderShape());
-}
-
-template ICGetElem_NativePrototypeCallScripted<JS::Symbol*>*
-ICGetElem_NativePrototypeCallScripted<JS::Symbol*>::Clone(JSContext*, ICStubSpace*, ICStub*,
-                                        ICGetElem_NativePrototypeCallScripted<JS::Symbol*>&);
-template ICGetElem_NativePrototypeCallScripted<js::PropertyName*>*
-ICGetElem_NativePrototypeCallScripted<js::PropertyName*>::Clone(JSContext*, ICStubSpace*, ICStub*,
-                                        ICGetElem_NativePrototypeCallScripted<js::PropertyName*>&);
-
 ICGetElem_Dense::ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape)
     : ICMonitoredStub(GetElem_Dense, stubCode, firstMonitorStub),
       shape_(shape)
 { }
 
 /* static */ ICGetElem_Dense*
 ICGetElem_Dense::Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
                        ICGetElem_Dense& other)
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -414,421 +414,16 @@ class ICGetElem_Fallback : public ICMoni
                 return nullptr;
             if (!stub->initMonitoringChain(cx, space, engine_))
                 return nullptr;
             return stub;
         }
     };
 };
 
-class ICGetElemNativeStub : public ICMonitoredStub
-{
-  public:
-    enum AccessType { FixedSlot = 0, DynamicSlot, UnboxedProperty, NativeGetter, ScriptedGetter, NumAccessTypes };
-
-  protected:
-    HeapReceiverGuard receiverGuard_;
-
-    static const unsigned NEEDS_ATOMIZE_SHIFT = 0;
-    static const uint16_t NEEDS_ATOMIZE_MASK = 0x1;
-
-    static const unsigned ACCESSTYPE_SHIFT = 1;
-    static const uint16_t ACCESSTYPE_MASK = 0x7;
-
-    static const unsigned ISSYMBOL_SHIFT = 4;
-    static const uint16_t ISSYMBOL_MASK = 0x1;
-
-    static_assert(ACCESSTYPE_MASK >= NumAccessTypes, "ACCESSTYPE_MASK must cover all possible AccessType values");
-
-    ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                        ReceiverGuard guard, AccessType acctype, bool needsAtomize, bool isSymbol);
-
-    ~ICGetElemNativeStub();
-
-  public:
-    HeapReceiverGuard& receiverGuard() {
-        return receiverGuard_;
-    }
-    static size_t offsetOfReceiverGuard() {
-        return offsetof(ICGetElemNativeStub, receiverGuard_);
-    }
-
-    AccessType accessType() const {
-        return static_cast<AccessType>((extra_ >> ACCESSTYPE_SHIFT) & ACCESSTYPE_MASK);
-    }
-
-    bool needsAtomize() const {
-        return (extra_ >> NEEDS_ATOMIZE_SHIFT) & NEEDS_ATOMIZE_MASK;
-    }
-
-    bool isSymbol() const {
-        return (extra_ >> ISSYMBOL_SHIFT) & ISSYMBOL_MASK;
-    }
-};
-
-template <class T>
-class ICGetElemNativeStubImpl : public ICGetElemNativeStub
-{
-  protected:
-    GCPtr<T> key_;
-
-    ICGetElemNativeStubImpl(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                            ReceiverGuard guard, const T* key, AccessType acctype, bool needsAtomize)
-      : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, acctype, needsAtomize,
-                            mozilla::IsSame<T, JS::Symbol*>::value),
-        key_(*key)
-    {}
-
-  public:
-    GCPtr<T>& key() {
-        return key_;
-    }
-    static size_t offsetOfKey() {
-        return offsetof(ICGetElemNativeStubImpl, key_);
-    }
-};
-
-typedef ICGetElemNativeStub::AccessType AccType;
-
-template <class T>
-class ICGetElemNativeSlotStub : public ICGetElemNativeStubImpl<T>
-{
-  protected:
-    uint32_t offset_;
-
-    ICGetElemNativeSlotStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                            ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
-                            uint32_t offset)
-      : ICGetElemNativeStubImpl<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize),
-        offset_(offset)
-    {
-        MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName ||
-                   kind == ICStub::GetElem_NativeSlotSymbol ||
-                   kind == ICStub::GetElem_NativePrototypeSlotName ||
-                   kind == ICStub::GetElem_NativePrototypeSlotSymbol ||
-                   kind == ICStub::GetElem_UnboxedPropertyName);
-        MOZ_ASSERT(acctype == ICGetElemNativeStub::FixedSlot ||
-                   acctype == ICGetElemNativeStub::DynamicSlot ||
-                   acctype == ICGetElemNativeStub::UnboxedProperty);
-    }
-
-  public:
-    uint32_t offset() const {
-        return offset_;
-    }
-
-    static size_t offsetOfOffset() {
-        return offsetof(ICGetElemNativeSlotStub, offset_);
-    }
-};
-
-template <class T>
-class ICGetElemNativeGetterStub : public ICGetElemNativeStubImpl<T>
-{
-  protected:
-    GCPtrFunction getter_;
-    uint32_t pcOffset_;
-
-    ICGetElemNativeGetterStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                              ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
-                              JSFunction* getter, uint32_t pcOffset);
-
-  public:
-    GCPtrFunction& getter() {
-        return getter_;
-    }
-    static size_t offsetOfGetter() {
-        return offsetof(ICGetElemNativeGetterStub, getter_);
-    }
-
-    static size_t offsetOfPCOffset() {
-        return offsetof(ICGetElemNativeGetterStub, pcOffset_);
-    }
-};
-
-template <class T>
-ICStub::Kind
-getGetElemStubKind(ICStub::Kind kind)
-{
-    MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName ||
-               kind == ICStub::GetElem_NativePrototypeSlotName ||
-               kind == ICStub::GetElem_NativePrototypeCallNativeName ||
-               kind == ICStub::GetElem_NativePrototypeCallScriptedName);
-    return static_cast<ICStub::Kind>(kind + mozilla::IsSame<T, JS::Symbol*>::value);
-}
-
-template <class T>
-class ICGetElem_NativeSlot : public ICGetElemNativeSlotStub<T>
-{
-    friend class ICStubSpace;
-    ICGetElem_NativeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
-                         const T* key, AccType acctype, bool needsAtomize, uint32_t offset)
-      : ICGetElemNativeSlotStub<T>(getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
-                                   stubCode, firstMonitorStub, guard,
-                                   key, acctype, needsAtomize, offset)
-    {}
-};
-
-class ICGetElem_NativeSlotName :
-      public ICGetElem_NativeSlot<PropertyName*>
-{};
-class ICGetElem_NativeSlotSymbol :
-      public ICGetElem_NativeSlot<JS::Symbol*>
-{};
-
-template <class T>
-class ICGetElem_UnboxedProperty : public ICGetElemNativeSlotStub<T>
-{
-    friend class ICStubSpace;
-    ICGetElem_UnboxedProperty(JitCode* stubCode, ICStub* firstMonitorStub,
-                              ReceiverGuard guard, const T* key, AccType acctype,
-                              bool needsAtomize, uint32_t offset)
-      : ICGetElemNativeSlotStub<T>(ICStub::GetElem_UnboxedPropertyName, stubCode, firstMonitorStub,
-                                   guard, key, acctype, needsAtomize, offset)
-     {}
-};
-
-class ICGetElem_UnboxedPropertyName :
-      public ICGetElem_UnboxedProperty<PropertyName*>
-{};
-
-template <class T>
-class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub<T>
-{
-    friend class ICStubSpace;
-    GCPtrObject holder_;
-    GCPtrShape holderShape_;
-
-    ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
-                                  const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
-                                  JSObject* holder, Shape* holderShape);
-
-  public:
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICGetElem_NativePrototypeSlot, holder_);
-    }
-
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICGetElem_NativePrototypeSlot, holderShape_);
-    }
-};
-
-class ICGetElem_NativePrototypeSlotName :
-      public ICGetElem_NativePrototypeSlot<PropertyName*>
-{};
-class ICGetElem_NativePrototypeSlotSymbol :
-      public ICGetElem_NativePrototypeSlot<JS::Symbol*>
-{};
-
-template <class T>
-class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub<T>
-{
-    friend class ICStubSpace;
-    GCPtrObject holder_;
-    GCPtrShape holderShape_;
-
-  protected:
-    ICGetElemNativePrototypeCallStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                     ReceiverGuard guard, const T* key, AccType acctype,
-                                     bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
-                                     JSObject* holder, Shape* holderShape);
-
-  public:
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICGetElemNativePrototypeCallStub, holder_);
-    }
-
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICGetElemNativePrototypeCallStub, holderShape_);
-    }
-};
-
-template <class T>
-class ICGetElem_NativePrototypeCallNative : public ICGetElemNativePrototypeCallStub<T>
-{
-    friend class ICStubSpace;
-
-    ICGetElem_NativePrototypeCallNative(JitCode* stubCode, ICStub* firstMonitorStub,
-                                        ReceiverGuard guard, const T* key, AccType acctype,
-                                        bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
-                                        JSObject* holder, Shape* holderShape)
-      : ICGetElemNativePrototypeCallStub<T>(getGetElemStubKind<T>(
-                                            ICStub::GetElem_NativePrototypeCallNativeName),
-                                            stubCode, firstMonitorStub, guard, key,
-                                            acctype, needsAtomize, getter, pcOffset, holder,
-                                            holderShape)
-    {}
-
-  public:
-    static ICGetElem_NativePrototypeCallNative<T>* Clone(JSContext* cx, ICStubSpace* space,
-                                                         ICStub* firstMonitorStub,
-                                                         ICGetElem_NativePrototypeCallNative<T>& other);
-};
-
-class ICGetElem_NativePrototypeCallNativeName :
-      public ICGetElem_NativePrototypeCallNative<PropertyName*>
-{};
-class ICGetElem_NativePrototypeCallNativeSymbol :
-      public ICGetElem_NativePrototypeCallNative<JS::Symbol*>
-{};
-
-template <class T>
-class ICGetElem_NativePrototypeCallScripted : public ICGetElemNativePrototypeCallStub<T>
-{
-    friend class ICStubSpace;
-
-    ICGetElem_NativePrototypeCallScripted(JitCode* stubCode, ICStub* firstMonitorStub,
-                                          ReceiverGuard guard, const T* key, AccType acctype,
-                                          bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
-                                          JSObject* holder, Shape* holderShape)
-      : ICGetElemNativePrototypeCallStub<T>(getGetElemStubKind<T>(
-                                            ICStub::GetElem_NativePrototypeCallScriptedName),
-                                            stubCode, firstMonitorStub, guard, key, acctype,
-                                            needsAtomize, getter, pcOffset, holder, holderShape)
-    {}
-
-  public:
-    static ICGetElem_NativePrototypeCallScripted<T>*
-    Clone(JSContext* cx, ICStubSpace* space,
-          ICStub* firstMonitorStub,
-          ICGetElem_NativePrototypeCallScripted<T>& other);
-};
-
-class ICGetElem_NativePrototypeCallScriptedName :
-      public ICGetElem_NativePrototypeCallScripted<PropertyName*>
-{};
-class ICGetElem_NativePrototypeCallScriptedSymbol :
-      public ICGetElem_NativePrototypeCallScripted<JS::Symbol*>
-{};
-
-// Compiler for GetElem_NativeSlot and GetElem_NativePrototypeSlot stubs.
-template <class T>
-class ICGetElemNativeCompiler : public ICStubCompiler
-{
-    ICStub* firstMonitorStub_;
-    HandleObject obj_;
-    HandleObject holder_;
-    Handle<T> key_;
-    AccType acctype_;
-    bool needsAtomize_;
-    uint32_t offset_;
-    JSValueType unboxedType_;
-    HandleFunction getter_;
-    uint32_t pcOffset_;
-
-    MOZ_MUST_USE bool emitCheckKey(MacroAssembler& masm, Label& failure);
-    MOZ_MUST_USE bool emitCallNative(MacroAssembler& masm, Register objReg);
-    MOZ_MUST_USE bool emitCallScripted(MacroAssembler& masm, Register objReg);
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-  protected:
-    virtual int32_t getKey() const {
-        MOZ_ASSERT(static_cast<int32_t>(acctype_) <= 7);
-        MOZ_ASSERT(static_cast<int32_t>(unboxedType_) <= 15);
-        return static_cast<int32_t>(engine_) |
-              (static_cast<int32_t>(kind) << 1) |
-              (static_cast<int32_t>(needsAtomize_) << 17) |
-              (static_cast<int32_t>(acctype_) << 18) |
-              (static_cast<int32_t>(unboxedType_) << 21) |
-              (static_cast<int32_t>(mozilla::IsSame<JS::Symbol*, T>::value) << 25) |
-              (HeapReceiverGuard::keyBits(obj_) << 26);
-    }
-
-  public:
-    ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub,
-                            HandleObject obj, HandleObject holder, Handle<T> key, AccType acctype,
-                            bool needsAtomize, uint32_t offset,
-                            JSValueType unboxedType = JSVAL_TYPE_MAGIC)
-      : ICStubCompiler(cx, kind, Engine::Baseline),
-        firstMonitorStub_(firstMonitorStub),
-        obj_(obj),
-        holder_(holder),
-        key_(key),
-        acctype_(acctype),
-        needsAtomize_(needsAtomize),
-        offset_(offset),
-        unboxedType_(unboxedType),
-        getter_(nullptr),
-        pcOffset_(0)
-    {}
-
-    ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub,
-                            HandleObject obj, HandleObject holder, Handle<T> key, AccType acctype,
-                            bool needsAtomize, HandleFunction getter, uint32_t pcOffset)
-      : ICStubCompiler(cx, kind, Engine::Baseline),
-        firstMonitorStub_(firstMonitorStub),
-        obj_(obj),
-        holder_(holder),
-        key_(key),
-        acctype_(acctype),
-        needsAtomize_(needsAtomize),
-        offset_(0),
-        unboxedType_(JSVAL_TYPE_MAGIC),
-        getter_(getter),
-        pcOffset_(pcOffset)
-    {}
-
-    ICStub* getStub(ICStubSpace* space) {
-        RootedReceiverGuard guard(cx, ReceiverGuard(obj_));
-        if (kind == ICStub::GetElem_NativeSlotName || kind == ICStub::GetElem_NativeSlotSymbol) {
-            MOZ_ASSERT(obj_ == holder_);
-            return newStub<ICGetElem_NativeSlot<T>>(
-                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
-                    needsAtomize_, offset_);
-        }
-
-	if (kind == ICStub::GetElem_UnboxedPropertyName) {
-            MOZ_ASSERT(obj_ == holder_);
-            return newStub<ICGetElem_UnboxedProperty<T>>(
-                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
-                    needsAtomize_, offset_);
-        }
-
-        MOZ_ASSERT(obj_ != holder_);
-        RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
-        if (kind == ICStub::GetElem_NativePrototypeSlotName ||
-            kind == ICStub::GetElem_NativePrototypeSlotSymbol)
-        {
-            return newStub<ICGetElem_NativePrototypeSlot<T>>(
-                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
-                    needsAtomize_, offset_, holder_, holderShape);
-        }
-
-        if (kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
-            kind == ICStub::GetElem_NativePrototypeCallNativeName) {
-            return newStub<ICGetElem_NativePrototypeCallNative<T>>(
-                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
-                    needsAtomize_, getter_, pcOffset_, holder_, holderShape);
-        }
-
-        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
-                   kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
-        if (kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
-            kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol) {
-            return newStub<ICGetElem_NativePrototypeCallScripted<T>>(
-                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
-                    needsAtomize_, getter_, pcOffset_, holder_, holderShape);
-        }
-
-        MOZ_CRASH("Invalid kind.");
-    }
-};
-
 class ICGetElem_String : public ICStub
 {
     friend class ICStubSpace;
 
     explicit ICGetElem_String(JitCode* stubCode)
       : ICStub(ICStub::GetElem_String, stubCode) {}
 
   public:
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -44,25 +44,16 @@ namespace jit {
     _(Call_ClassHook)                            \
     _(Call_ScriptedApplyArray)                   \
     _(Call_ScriptedApplyArguments)               \
     _(Call_ScriptedFunCall)                      \
     _(Call_StringSplit)                          \
     _(Call_IsSuspendedStarGenerator)             \
                                                  \
     _(GetElem_Fallback)                          \
-    _(GetElem_NativeSlotName)                    \
-    _(GetElem_NativeSlotSymbol)                  \
-    _(GetElem_NativePrototypeSlotName)           \
-    _(GetElem_NativePrototypeSlotSymbol)         \
-    _(GetElem_NativePrototypeCallNativeName)     \
-    _(GetElem_NativePrototypeCallNativeSymbol)   \
-    _(GetElem_NativePrototypeCallScriptedName)   \
-    _(GetElem_NativePrototypeCallScriptedSymbol) \
-    _(GetElem_UnboxedPropertyName)               \
     _(GetElem_String)                            \
     _(GetElem_Dense)                             \
     _(GetElem_UnboxedArray)                      \
     _(GetElem_TypedArray)                        \
     _(GetElem_Arguments)                         \
                                                  \
     _(SetElem_Fallback)                          \
     _(SetElem_DenseOrUnboxedArray)               \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -1015,25 +1015,16 @@ BaselineInspector::expectedPropertyAcces
 
           case ICStub::GetProp_Generic:
             return MIRType::Value;
 
           case ICStub::GetElem_Arguments:
             // Either an object or magic arguments.
             return MIRType::Value;
 
-          case ICStub::GetElem_NativeSlotName:
-          case ICStub::GetElem_NativeSlotSymbol:
-          case ICStub::GetElem_NativePrototypeSlotName:
-          case ICStub::GetElem_NativePrototypeSlotSymbol:
-          case ICStub::GetElem_NativePrototypeCallNativeName:
-          case ICStub::GetElem_NativePrototypeCallNativeSymbol:
-          case ICStub::GetElem_NativePrototypeCallScriptedName:
-          case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
-          case ICStub::GetElem_UnboxedPropertyName:
           case ICStub::GetElem_String:
           case ICStub::GetElem_Dense:
           case ICStub::GetElem_TypedArray:
           case ICStub::GetElem_UnboxedArray:
             stubType = MIRType::Object;
             break;
 
           case ICStub::CacheIR_Monitored:
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -14,26 +14,28 @@
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
 
 GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, jsbytecode* pc, ICStubEngine engine,
+                                       CacheKind cacheKind,
                                        bool* isTemporarilyUnoptimizable,
                                        HandleValue val, HandleValue idVal,
                                        MutableHandleValue res)
   : writer(cx),
     cx_(cx),
     pc_(pc),
     val_(val),
     idVal_(idVal),
     res_(res),
     engine_(engine),
+    cacheKind_(cacheKind),
     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
     preliminaryObjectAction_(PreliminaryObjectAction::None)
 {}
 
 static void
 EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
                    Shape* shape)
 {
@@ -46,48 +48,60 @@ EmitLoadSlotResult(CacheIRWriter& writer
 }
 
 bool
 GetPropIRGenerator::tryAttachStub()
 {
     AutoAssertNoPendingException aanpe(cx_);
 
     ValOperandId valId(writer.setInputOperandId(0));
+    if (cacheKind_ == CacheKind::GetElem) {
+        MOZ_ASSERT(getElemKeyValueId().id() == 1);
+        writer.setInputOperandId(1);
+    }
 
-    RootedId id(cx_, INTERNED_STRING_TO_JSID(cx_, idVal_.toString())); //XXX
+    RootedId id(cx_);
+    bool nameOrSymbol;
+    if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
+        cx_->clearPendingException();
+        return false;
+    }
 
     if (val_.isObject()) {
         RootedObject obj(cx_, &val_.toObject());
         ObjOperandId objId = writer.guardIsObject(valId);
-
-        if (tryAttachObjectLength(obj, objId, id))
-            return true;
-        if (tryAttachNative(obj, objId, id))
-            return true;
-        if (tryAttachUnboxed(obj, objId, id))
-            return true;
-        if (tryAttachUnboxedExpando(obj, objId, id))
-            return true;
-        if (tryAttachTypedObject(obj, objId, id))
-            return true;
-        if (tryAttachModuleNamespace(obj, objId, id))
-            return true;
-        if (tryAttachWindowProxy(obj, objId, id))
-            return true;
-        if (tryAttachProxy(obj, objId, id))
-            return true;
+        if (nameOrSymbol) {
+            if (tryAttachObjectLength(obj, objId, id))
+                return true;
+            if (tryAttachNative(obj, objId, id))
+                return true;
+            if (tryAttachUnboxed(obj, objId, id))
+                return true;
+            if (tryAttachUnboxedExpando(obj, objId, id))
+                return true;
+            if (tryAttachTypedObject(obj, objId, id))
+                return true;
+            if (tryAttachModuleNamespace(obj, objId, id))
+                return true;
+            if (tryAttachWindowProxy(obj, objId, id))
+                return true;
+            if (tryAttachProxy(obj, objId, id))
+                return true;
+        }
         return false;
     }
 
-    if (tryAttachPrimitive(valId, id))
-        return true;
-    if (tryAttachStringLength(valId, id))
-        return true;
-    if (tryAttachMagicArguments(valId, id))
-        return true;
+    if (nameOrSymbol) {
+        if (tryAttachPrimitive(valId, id))
+            return true;
+        if (tryAttachStringLength(valId, id))
+            return true;
+        if (tryAttachMagicArguments(valId, id))
+            return true;
+    }
 
     return false;
 }
 
 static bool
 IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
                       jsbytecode* pc)
 {
@@ -309,30 +323,32 @@ GetPropIRGenerator::tryAttachNative(Hand
     RootedNativeObject holder(cx_);
 
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
                                                             engine_, isTemporarilyUnoptimizable_);
     switch (type) {
       case CanAttachNone:
         return false;
       case CanAttachReadSlot:
+        maybeEmitIdGuard(id);
         if (holder) {
             EnsureTrackPropertyTypes(cx_, holder, id);
             if (obj == holder) {
                 // See the comment in StripPreliminaryObjectStubs.
                 if (IsPreliminaryObject(obj))
                     preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
                 else
                     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
             }
         }
         EmitReadSlotResult(writer, obj, holder, shape, objId);
         EmitReadSlotReturn(writer, obj, holder, shape);
         return true;
       case CanAttachCallGetter:
+        maybeEmitIdGuard(id);
         EmitCallGetterResult(writer, obj, holder, shape, objId);
         return true;
     }
 
     MOZ_CRASH("Bad NativeGetPropCacheability");
 }
 
 bool
@@ -367,16 +383,17 @@ GetPropIRGenerator::tryAttachWindowProxy
     // instead of the WindowProxy as |this| value.
     JSFunction* callee = &shape->getterObject()->as<JSFunction>();
     MOZ_ASSERT(callee->isNative());
     if (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())
         return false;
 
     // Guard the incoming object is a WindowProxy and inline a getter call based
     // on the Window object.
+    maybeEmitIdGuard(id);
     writer.guardClass(objId, GuardClassKind::WindowProxy);
     ObjOperandId windowObjId = writer.loadObject(windowObj);
     EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId);
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id)
@@ -384,26 +401,36 @@ GetPropIRGenerator::tryAttachGenericProx
     MOZ_ASSERT(obj->is<ProxyObject>());
 
     writer.guardIsProxy(objId);
 
     // Ensure that the incoming object is not a DOM proxy, so that we can get to
     // the specialized stubs
     writer.guardNotDOMProxy(objId);
 
-    writer.callProxyGetResult(objId, id);
+    if (cacheKind_ == CacheKind::GetProp) {
+        writer.callProxyGetResult(objId, id);
+    } else {
+        // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
+        // but for GetElem we prefer to attach a stub that can handle any Value
+        // so we don't attach a new stub for every id.
+        MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+        writer.callProxyGetByValueResult(objId, getElemKeyValueId());
+    }
+
     writer.typeMonitorResult();
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
 {
     MOZ_ASSERT(IsCacheableDOMProxy(obj));
 
+    maybeEmitIdGuard(id);
     writer.guardShape(objId, obj->maybeShape());
 
     // No need for more guards: we know this is a DOM proxy, since the shape
     // guard enforces a given JSClass, so just go ahead and emit the call to
     // ProxyGet.
     writer.callProxyGetResult(objId, id);
     writer.typeMonitorResult();
     return true;
@@ -453,16 +480,17 @@ GetPropIRGenerator::tryAttachDOMProxyUns
     RootedShape shape(cx_);
 
     NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
                                                                 pc_, engine_,
                                                                 isTemporarilyUnoptimizable_);
     if (canCache == CanAttachNone)
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardShape(objId, obj->maybeShape());
 
     // Guard that our expando object hasn't started shadowing this property.
     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
 
     if (holder) {
         // Found the property on the prototype chain. Treat it like a native
         // getprop.
@@ -524,16 +552,17 @@ GetPropIRGenerator::tryAttachUnboxed(Han
 
     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
     if (!property)
         return false;
 
     if (!cx_->runtime()->jitSupportsFloatingPoint)
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardGroup(objId, obj->group());
     writer.loadUnboxedPropertyResult(objId, property->type,
                                      UnboxedPlainObject::offsetOfData() + property->offset);
     if (property->type == JSVAL_TYPE_OBJECT)
         writer.typeMonitorResult();
     else
         writer.returnFromIC();
 
@@ -550,16 +579,17 @@ GetPropIRGenerator::tryAttachUnboxedExpa
     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
     if (!expando)
         return false;
 
     Shape* shape = expando->lookup(cx_, id);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return false;
 
+    maybeEmitIdGuard(id);
     EmitReadSlotResult(writer, obj, obj, shape, objId);
     EmitReadSlotReturn(writer, obj, obj, shape);
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id)
 {
@@ -584,16 +614,17 @@ GetPropIRGenerator::tryAttachTypedObject
         return false;
 
     Shape* shape = typedObj->maybeShape();
     TypedThingLayout layout = GetTypedThingLayout(shape->getObjectClass());
 
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
     uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
 
+    maybeEmitIdGuard(id);
     writer.guardNoDetachedTypedObjects();
     writer.guardShape(objId, shape);
     writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr);
 
     // Only monitor the result if the type produced by this stub might vary.
     bool monitorLoad = false;
     if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
         Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
@@ -618,30 +649,33 @@ GetPropIRGenerator::tryAttachObjectLengt
         return false;
 
     if (obj->is<ArrayObject>()) {
         // Make sure int32 is added to the TypeSet before we attach a stub, so
         // the stub can return int32 values without monitoring the result.
         if (obj->as<ArrayObject>().length() > INT32_MAX)
             return false;
 
+        maybeEmitIdGuard(id);
         writer.guardClass(objId, GuardClassKind::Array);
         writer.loadInt32ArrayLengthResult(objId);
         writer.returnFromIC();
         return true;
     }
 
     if (obj->is<UnboxedArrayObject>()) {
+        maybeEmitIdGuard(id);
         writer.guardClass(objId, GuardClassKind::UnboxedArray);
         writer.loadUnboxedArrayLengthResult(objId);
         writer.returnFromIC();
         return true;
     }
 
     if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
+        maybeEmitIdGuard(id);
         if (obj->is<MappedArgumentsObject>()) {
             writer.guardClass(objId, GuardClassKind::MappedArguments);
         } else {
             MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
             writer.guardClass(objId, GuardClassKind::UnmappedArguments);
         }
         writer.loadArgumentsObjectLengthResult(objId);
         writer.returnFromIC();
@@ -666,16 +700,17 @@ GetPropIRGenerator::tryAttachModuleNames
     // Don't emit a stub until the target binding has been initialized.
     if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
         return false;
 
     if (IsIonEnabled(cx_))
         EnsureTrackPropertyTypes(cx_, env, shape->propid());
 
     // Check for the specific namespace object.
+    maybeEmitIdGuard(id);
     writer.guardSpecificObject(objId, ns);
 
     ObjOperandId envId = writer.loadObject(env);
     EmitLoadSlotResult(writer, envId, env, shape);
     writer.typeMonitorResult();
     return true;
 }
 
@@ -712,51 +747,76 @@ GetPropIRGenerator::tryAttachPrimitive(V
         EnsureTrackPropertyTypes(cx_, proto, id);
 
     // For now, only look for properties directly set on the prototype.
     Shape* shape = proto->lookup(cx_, id);
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return false;
 
     writer.guardType(valId, primitiveType);
+    maybeEmitIdGuard(id);
 
     ObjOperandId protoId = writer.loadObject(proto);
     writer.guardShape(protoId, proto->lastProperty());
     EmitLoadSlotResult(writer, protoId, proto, shape);
     writer.typeMonitorResult();
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id)
 {
     if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length))
         return false;
 
     StringOperandId strId = writer.guardIsString(valId);
+    maybeEmitIdGuard(id);
     writer.loadStringLengthResult(strId);
     writer.returnFromIC();
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachMagicArguments(ValOperandId valId, HandleId id)
 {
     if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
         return false;
 
     if (!JSID_IS_ATOM(id, cx_->names().length) && !JSID_IS_ATOM(id, cx_->names().callee))
         return false;
 
+    maybeEmitIdGuard(id);
     writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
     writer.guardFrameHasNoArgumentsObject();
 
     if (JSID_IS_ATOM(id, cx_->names().length)) {
         writer.loadFrameNumActualArgsResult();
         writer.returnFromIC();
     } else {
         MOZ_ASSERT(JSID_IS_ATOM(id, cx_->names().callee));
         writer.loadFrameCalleeResult();
         writer.typeMonitorResult();
     }
 
     return true;
 }
+
+void
+GetPropIRGenerator::maybeEmitIdGuard(jsid id)
+{
+    if (cacheKind_ == CacheKind::GetProp) {
+        // Constant PropertyName, no guards necessary.
+        MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
+        return;
+    }
+
+    MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+
+    ValOperandId valId = getElemKeyValueId();
+    if (JSID_IS_SYMBOL(id)) {
+        SymbolOperandId symId = writer.guardIsSymbol(valId);
+        writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
+    } else {
+        MOZ_ASSERT(JSID_IS_ATOM(id));
+        StringOperandId strId = writer.guardIsString(valId);
+        writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
+    }
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -81,42 +81,61 @@ class ObjOperandId : public OperandId
 
 class StringOperandId : public OperandId
 {
   public:
     StringOperandId() = default;
     explicit StringOperandId(uint16_t id) : OperandId(id) {}
 };
 
+class SymbolOperandId : public OperandId
+{
+  public:
+    SymbolOperandId() = default;
+    explicit SymbolOperandId(uint16_t id) : OperandId(id) {}
+};
+
 class TypedOperandId : public OperandId
 {
     JSValueType type_;
 
   public:
     MOZ_IMPLICIT TypedOperandId(ObjOperandId id)
       : OperandId(id.id()), type_(JSVAL_TYPE_OBJECT)
     {}
     MOZ_IMPLICIT TypedOperandId(StringOperandId id)
       : OperandId(id.id()), type_(JSVAL_TYPE_STRING)
     {}
+    MOZ_IMPLICIT TypedOperandId(SymbolOperandId id)
+      : OperandId(id.id()), type_(JSVAL_TYPE_SYMBOL)
+    {}
 
     JSValueType type() const { return type_; }
 };
 
+enum class CacheKind : uint8_t
+{
+    GetProp,
+    GetElem,
+};
+
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsString)                      \
+    _(GuardIsSymbol)                      \
     _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         \
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificObject)                \
+    _(GuardSpecificAtom)                  \
+    _(GuardSpecificSymbol)                \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardMagicValue)                    \
     _(GuardFrameHasNoArgumentsObject)     \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(LoadObject)                         \
     _(LoadProto)                          \
                                           \
@@ -133,16 +152,17 @@ class TypedOperandId : public OperandId
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadStringLengthResult)             \
     _(LoadFrameCalleeResult)              \
     _(LoadFrameNumActualArgsResult)       \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
     _(CallProxyGetResult)                 \
+    _(CallProxyGetByValueResult)          \
     _(LoadUndefinedResult)                \
                                           \
     _(TypeMonitorResult)                  \
     _(ReturnFromIC)
 
 enum class CacheOp {
 #define DEFINE_OP(op) op,
     CACHE_IR_OPS(DEFINE_OP)
@@ -153,16 +173,18 @@ class StubField
 {
   public:
     enum class Type : uint8_t {
         // These fields take up a single word.
         RawWord,
         Shape,
         ObjectGroup,
         JSObject,
+        Symbol,
+        String,
         Id,
 
         // These fields take up 64 bits on all platforms.
         RawInt64,
         First64BitType = RawInt64,
         Value,
 
         Limit
@@ -337,16 +359,20 @@ class MOZ_RAII CacheIRWriter : public JS
     ObjOperandId guardIsObject(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsObject, val);
         return ObjOperandId(val.id());
     }
     StringOperandId guardIsString(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsString, val);
         return StringOperandId(val.id());
     }
+    SymbolOperandId guardIsSymbol(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsSymbol, val);
+        return SymbolOperandId(val.id());
+    }
     void guardType(ValOperandId val, JSValueType type) {
         writeOpWithOperandId(CacheOp::GuardType, val);
         static_assert(sizeof(type) == sizeof(uint8_t), "JSValueType should fit in a byte");
         buffer_.writeByte(uint32_t(type));
     }
     void guardShape(ObjOperandId obj, Shape* shape) {
         writeOpWithOperandId(CacheOp::GuardShape, obj);
         addStubField(uintptr_t(shape), StubField::Type::Shape);
@@ -370,16 +396,24 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     void guardNotDOMProxy(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj);
     }
     void guardSpecificObject(ObjOperandId obj, JSObject* expected) {
         writeOpWithOperandId(CacheOp::GuardSpecificObject, obj);
         addStubField(uintptr_t(expected), StubField::Type::JSObject);
     }
+    void guardSpecificAtom(StringOperandId str, JSAtom* expected) {
+        writeOpWithOperandId(CacheOp::GuardSpecificAtom, str);
+        addStubField(uintptr_t(expected), StubField::Type::String);
+    }
+    void guardSpecificSymbol(SymbolOperandId sym, JS::Symbol* expected) {
+        writeOpWithOperandId(CacheOp::GuardSpecificSymbol, sym);
+        addStubField(uintptr_t(expected), StubField::Type::Symbol);
+    }
     void guardMagicValue(ValOperandId val, JSWhyMagic magic) {
         writeOpWithOperandId(CacheOp::GuardMagicValue, val);
         buffer_.writeByte(uint32_t(magic));
     }
     void guardNoDetachedTypedObjects() {
         writeOp(CacheOp::GuardNoDetachedTypedObjects);
     }
     void guardFrameHasNoArgumentsObject() {
@@ -480,16 +514,20 @@ class MOZ_RAII CacheIRWriter : public JS
     void callNativeGetterResult(ObjOperandId obj, JSFunction* getter) {
         writeOpWithOperandId(CacheOp::CallNativeGetterResult, obj);
         addStubField(uintptr_t(getter), StubField::Type::JSObject);
     }
     void callProxyGetResult(ObjOperandId obj, jsid id) {
         writeOpWithOperandId(CacheOp::CallProxyGetResult, obj);
         addStubField(uintptr_t(JSID_BITS(id)), StubField::Type::Id);
     }
+    void callProxyGetByValueResult(ObjOperandId obj, ValOperandId idVal) {
+        writeOpWithOperandId(CacheOp::CallProxyGetByValueResult, obj);
+        writeOperandId(idVal);
+    }
 
     void typeMonitorResult() {
         writeOp(CacheOp::TypeMonitorResult);
     }
     void returnFromIC() {
         writeOp(CacheOp::ReturnFromIC);
     }
 };
@@ -517,16 +555,17 @@ class MOZ_RAII CacheIRReader
 
     CacheOp readOp() {
         return CacheOp(buffer_.readByte());
     }
 
     ValOperandId valOperandId() { return ValOperandId(buffer_.readByte()); }
     ObjOperandId objOperandId() { return ObjOperandId(buffer_.readByte()); }
     StringOperandId stringOperandId() { return StringOperandId(buffer_.readByte()); }
+    SymbolOperandId symbolOperandId() { return SymbolOperandId(buffer_.readByte()); }
 
     uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); }
     GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); }
     JSValueType valueType() { return JSValueType(buffer_.readByte()); }
     TypedThingLayout typedThingLayout() { return TypedThingLayout(buffer_.readByte()); }
     uint32_t typeDescrKey() { return buffer_.readByte(); }
     JSWhyMagic whyMagic() { return JSWhyMagic(buffer_.readByte()); }
 
@@ -559,16 +598,17 @@ class MOZ_RAII GetPropIRGenerator
 {
     CacheIRWriter writer;
     JSContext* cx_;
     jsbytecode* pc_;
     HandleValue val_;
     HandleValue idVal_;
     MutableHandleValue res_;
     ICStubEngine engine_;
+    CacheKind cacheKind_;
     bool* isTemporarilyUnoptimizable_;
 
     enum class PreliminaryObjectAction { None, Unlink, NotePreliminary };
     PreliminaryObjectAction preliminaryObjectAction_;
 
     bool tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id);
@@ -581,38 +621,42 @@ class MOZ_RAII GetPropIRGenerator
     bool tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id);
 
     bool tryAttachPrimitive(ValOperandId valId, HandleId id);
     bool tryAttachStringLength(ValOperandId valId, HandleId id);
     bool tryAttachMagicArguments(ValOperandId valId, HandleId id);
 
+    ValOperandId getElemKeyValueId() const {
+        MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
+        return ValOperandId(1);
+    }
+
+    // If this is a GetElem cache, emit instructions to guard the incoming Value
+    // matches |id|.
+    void maybeEmitIdGuard(jsid id);
+
     GetPropIRGenerator(const GetPropIRGenerator&) = delete;
     GetPropIRGenerator& operator=(const GetPropIRGenerator&) = delete;
 
   public:
-    GetPropIRGenerator(JSContext* cx, jsbytecode* pc, ICStubEngine engine,
+    GetPropIRGenerator(JSContext* cx, jsbytecode* pc, ICStubEngine engine, CacheKind cacheKind,
                        bool* isTemporarilyUnoptimizable,
                        HandleValue val, HandleValue idVal, MutableHandleValue res);
 
     bool tryAttachStub();
 
     bool shouldUnlinkPreliminaryObjectStubs() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
     }
     bool shouldNotePreliminaryObjectStub() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::NotePreliminary;
     }
-    const CacheIRWriter& writerRef() const {
-        return writer;
-    }
-};
 
-enum class CacheKind : uint8_t
-{
-    GetProp
+    const CacheIRWriter& writerRef() const { return writer; }
+    CacheKind cacheKind() const { return cacheKind_; }
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1353,30 +1353,16 @@ CanAttachNativeGetProp(JSContext* cx, co
         // Don't enable getter call if cache is idempotent, since they can be
         // effectful. This is handled by allowGetters()
         return GetPropertyIC::CanAttachCallGetter;
     }
 
     return GetPropertyIC::CanAttachNone;
 }
 
-static bool
-EqualStringsHelper(JSString* str1, JSString* str2)
-{
-    MOZ_ASSERT(str1->isAtom());
-    MOZ_ASSERT(!str2->isAtom());
-    MOZ_ASSERT(str1->length() == str2->length());
-
-    JSLinearString* str2Linear = str2->ensureLinear(nullptr);
-    if (!str2Linear)
-        return false;
-
-    return EqualChars(&str1->asLinear(), str2Linear);
-}
-
 static void
 EmitIdGuard(MacroAssembler& masm, jsid id, TypedOrValueRegister idReg, Register objReg,
             Register scratchReg, Label* failures)
 {
     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
 
     MOZ_ASSERT(idReg.type() == MIRType::String ||
                idReg.type() == MIRType::Symbol ||
@@ -2139,18 +2125,19 @@ GetPropertyIC::tryAttachModuleNamespace(
     Label* maybeFailures = failures.used() ? &failures : nullptr;
 
     GenerateReadModuleNamespace(cx, ion, masm, attacher, ns, env,
                                 shape, object(), output(), maybeFailures);
     return linkAndAttachStub(cx, masm, attacher, ion, "module namespace",
                              JS::TrackedOutcome::ICGetPropStub_ReadSlot);
 }
 
-static bool
-ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id, bool* nameOrSymbol)
+bool
+jit::ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
+                           bool* nameOrSymbol)
 {
     *nameOrSymbol = false;
 
     if (!idval.isString() && !idval.isSymbol())
         return true;
 
     if (!ValueToId<CanGC>(cx, idval, id))
         return false;
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -841,12 +841,15 @@ IONCACHE_KIND_LIST(CACHE_CASTS)
 
 bool IsCacheableProtoChainForIonOrCacheIR(JSObject* obj, JSObject* holder);
 bool IsCacheableGetPropReadSlotForIonOrCacheIR(JSObject* obj, JSObject* holder, Shape* shape);
 
 bool IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
                                     bool* isTemporarilyUnoptimizable = nullptr);
 bool IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape);
 
+bool ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
+                           bool* nameOrSymbol);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonCaches_h */
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -170,25 +170,16 @@ ICStub::NonCacheIRStubMakesGCCalls(Kind 
       case Call_AnyScripted:
       case Call_Native:
       case Call_ClassHook:
       case Call_ScriptedApplyArray:
       case Call_ScriptedApplyArguments:
       case Call_ScriptedFunCall:
       case Call_StringSplit:
       case WarmUpCounter_Fallback:
-      case GetElem_NativeSlotName:
-      case GetElem_NativeSlotSymbol:
-      case GetElem_NativePrototypeSlotName:
-      case GetElem_NativePrototypeSlotSymbol:
-      case GetElem_NativePrototypeCallNativeName:
-      case GetElem_NativePrototypeCallNativeSymbol:
-      case GetElem_NativePrototypeCallScriptedName:
-      case GetElem_NativePrototypeCallScriptedSymbol:
-      case GetElem_UnboxedPropertyName:
       case GetProp_CallNativeGlobal:
       case GetProp_Generic:
       case SetProp_CallScripted:
       case SetProp_CallNative:
       case RetSub_Fallback:
       // These two fallback stubs don't actually make non-tail calls,
       // but the fallback code for the bailout path needs to pop the stub frame
       // pushed during the bailout.
@@ -267,73 +258,16 @@ ICStub::trace(JSTracer* trc)
       }
       case ICStub::Call_StringSplit: {
         ICCall_StringSplit* callStub = toCall_StringSplit();
         TraceEdge(trc, &callStub->templateObject(), "baseline-callstringsplit-template");
         TraceEdge(trc, &callStub->expectedSep(), "baseline-callstringsplit-sep");
         TraceEdge(trc, &callStub->expectedStr(), "baseline-callstringsplit-str");
         break;
       }
-      case ICStub::GetElem_NativeSlotName:
-      case ICStub::GetElem_NativeSlotSymbol:
-      case ICStub::GetElem_UnboxedPropertyName: {
-        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
-        getElemStub->receiverGuard().trace(trc);
-        if (getElemStub->isSymbol()) {
-            ICGetElem_NativeSlot<JS::Symbol*>* typedGetElemStub = toGetElem_NativeSlotSymbol();
-            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key");
-        } else {
-            ICGetElemNativeSlotStub<PropertyName*>* typedGetElemStub =
-                reinterpret_cast<ICGetElemNativeSlotStub<PropertyName*>*>(this);
-            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key");
-        }
-        break;
-      }
-      case ICStub::GetElem_NativePrototypeSlotName:
-      case ICStub::GetElem_NativePrototypeSlotSymbol: {
-        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
-        getElemStub->receiverGuard().trace(trc);
-        if (getElemStub->isSymbol()) {
-            ICGetElem_NativePrototypeSlot<JS::Symbol*>* typedGetElemStub
-                = toGetElem_NativePrototypeSlotSymbol();
-            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key");
-            TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder");
-            TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape");
-        } else {
-            ICGetElem_NativePrototypeSlot<PropertyName*>* typedGetElemStub
-                = toGetElem_NativePrototypeSlotName();
-            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key");
-            TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder");
-            TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape");
-        }
-        break;
-      }
-      case ICStub::GetElem_NativePrototypeCallNativeName:
-      case ICStub::GetElem_NativePrototypeCallNativeSymbol:
-      case ICStub::GetElem_NativePrototypeCallScriptedName:
-      case ICStub::GetElem_NativePrototypeCallScriptedSymbol: {
-        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
-        getElemStub->receiverGuard().trace(trc);
-        if (getElemStub->isSymbol()) {
-            ICGetElemNativePrototypeCallStub<JS::Symbol*>* callStub =
-                reinterpret_cast<ICGetElemNativePrototypeCallStub<JS::Symbol*>*>(this);
-            TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key");
-            TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter");
-            TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder");
-            TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape");
-        } else {
-            ICGetElemNativePrototypeCallStub<PropertyName*>* callStub =
-                reinterpret_cast<ICGetElemNativePrototypeCallStub<PropertyName*>*>(this);
-            TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key");
-            TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter");
-            TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder");
-            TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape");
-        }
-        break;
-      }
       case ICStub::GetElem_Dense: {
         ICGetElem_Dense* getElemStub = toGetElem_Dense();
         TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape");
         break;
       }
       case ICStub::GetElem_UnboxedArray: {
         ICGetElem_UnboxedArray* getElemStub = toGetElem_UnboxedArray();
         TraceEdge(trc, &getElemStub->group(), "baseline-getelem-unboxed-array-group");
@@ -2400,19 +2334,20 @@ DoGetPropFallback(JSContext* cx, void* p
         if (!newStub)
             return false;
         stub->addNewStub(newStub);
         attached = true;
     }
 
     if (!attached && !JitOptions.disableCacheIR) {
         RootedValue idVal(cx, StringValue(name));
-        GetPropIRGenerator gen(cx, pc, engine, &isTemporarilyUnoptimizable, val, idVal, res);
+        GetPropIRGenerator gen(cx, pc, engine, CacheKind::GetProp, &isTemporarilyUnoptimizable,
+                               val, idVal, res);
         if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), CacheKind::GetProp,
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         engine, info.outerScript(cx), stub);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 attached = true;
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1357,10 +1357,36 @@ BaselineGetFunctionThis(JSContext* cx, B
 
 bool
 ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
 {
     RootedValue receiver(cx, ObjectValue(*proxy));
     return Proxy::get(cx, proxy, receiver, id, vp);
 }
 
+bool
+ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
+                        MutableHandleValue vp)
+{
+    RootedId id(cx);
+    if (!ValueToId<CanGC>(cx, idVal, &id))
+        return false;
+
+    RootedValue receiver(cx, ObjectValue(*proxy));
+    return Proxy::get(cx, proxy, receiver, id, vp);
+}
+
+bool
+EqualStringsHelper(JSString* str1, JSString* str2)
+{
+    MOZ_ASSERT(str1->isAtom());
+    MOZ_ASSERT(!str2->isAtom());
+    MOZ_ASSERT(str1->length() == str2->length());
+
+    JSLinearString* str2Linear = str2->ensureLinear(nullptr);
+    if (!str2Linear)
+        return false;
+
+    return EqualChars(&str1->asLinear(), str2Linear);
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -807,12 +807,19 @@ MOZ_MUST_USE bool
 ThrowObjectCoercible(JSContext* cx, HandleValue v);
 
 MOZ_MUST_USE bool
 BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
 
 MOZ_MUST_USE bool
 ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp);
 
+MOZ_MUST_USE bool
+ProxyGetPropertyByValue(JSContext* cx, HandleObject proxy, HandleValue idVal,
+                        MutableHandleValue vp);
+
+MOZ_MUST_USE bool
+EqualStringsHelper(JSString* str1, JSString* str2);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_VMFunctions_h */