Bug 1533890: Rewrite ConstStringSplit to work outside of microbenchmarks r=mgaudet
authorIain Ireland <iireland@mozilla.com>
Thu, 02 May 2019 06:57:27 +0000
changeset 531355 274e684c7979b29f1df7a5232248daa05643f714
parent 531354 98c5fbe185df062d9459c003dab7ce0d64da6003
child 531356 2ee2a6c2095d3dd7e56a87f44742cb2f63c950b9
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgaudet
bugs1533890
milestone68.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 1533890: Rewrite ConstStringSplit to work outside of microbenchmarks r=mgaudet While trying to pin down a performance regression, I realized that both the new and the old implementations of ConstStringSplit are broken. Three years ago, as part of some ES6 work, we changed String.prototype.split to call a self-hosted String_split function instead of native str_split. In turn, String_split calls intrinsic_StringSplitString in the case we care about. However, because the call to intrinsic_StringSplitString is in self-hosted code, we would only get value out of this stub if there was only one caller of String_split in the entire program. This patch changes ConstStringSplit to look for the self-hosted String_Split function, which means we can attach a stub to the user script instead of self-hosted code. It also adds support in BaselineInspector for extracting metadata about the string split from the stub, which enables the Ion version of this optimization. Depends on D29532 Differential Revision: https://phabricator.services.mozilla.com/D29533
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
js/src/vm/CommonPropertyNames.h
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -854,24 +854,40 @@ bool BaselineInspector::isOptimizableCon
   const ICEntry& entry = icEntryFromPC(pc);
 
   // If ConstStringSplit stub is attached, must have only one stub attached.
   if (entry.fallbackStub()->numOptimizedStubs() != 1) {
     return false;
   }
 
   ICStub* stub = entry.firstStub();
-  if (stub->kind() != ICStub::Call_ConstStringSplit) {
-    return false;
+
+  if (stub->kind() == ICStub::Call_ConstStringSplit) {
+    *strOut = stub->toCall_ConstStringSplit()->expectedStr();
+    *sepOut = stub->toCall_ConstStringSplit()->expectedSep();
+    *objOut = stub->toCall_ConstStringSplit()->templateObject();
+    return true;
   }
 
-  *strOut = stub->toCall_ConstStringSplit()->expectedStr();
-  *sepOut = stub->toCall_ConstStringSplit()->expectedSep();
-  *objOut = stub->toCall_ConstStringSplit()->templateObject();
-  return true;
+  if (ICStub::IsCacheIRKind(stub->kind())) {
+    mozilla::Maybe<CacheIRReader> argReader;
+    if (!MaybeArgumentReader(stub, CacheOp::MetaThreeByte, argReader) ||
+        argReader->metaKind<MetaThreeByteKind>() !=
+            MetaThreeByteKind::ConstStringSplitData) {
+      return false;
+    }
+    const CacheIRStubInfo* stubInfo = GetCacheIRStubInfo(stub);
+    *strOut = stubInfo->getStubField<JSString*>(stub, argReader->stubOffset());
+    *sepOut = stubInfo->getStubField<JSString*>(stub, argReader->stubOffset());
+    *objOut =
+        stubInfo->getStubField<ArrayObject*>(stub, argReader->stubOffset());
+    return true;
+  }
+
+  return false;
 }
 
 JSObject* BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc,
                                                            const Class* clasp) {
   if (!hasICScript()) {
     return nullptr;
   }
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -4584,22 +4584,16 @@ CallIRGenerator::CallIRGenerator(JSConte
       isFirstStub_(isFirstStub) {}
 
 AttachDecision CallIRGenerator::tryAttachStringSplit() {
   // Only optimize StringSplitString(str, str)
   if (argc_ != 2 || !args_[0].isString() || !args_[1].isString()) {
     return AttachDecision::NoAction;
   }
 
-  // If we have not previously attached a stub and both arguments are atoms,
-  // defer until after the call and attach a const string split stub.
-  if (isOptimizableConstStringSplit()) {
-    return AttachDecision::Deferred;
-  }
-
   // Get the object group to use for this location.
   RootedObjectGroup group(cx_,
                           ObjectGroupRealm::getStringSplitStringGroup(cx_));
   if (!group) {
     cx_->clearPendingException();
     return AttachDecision::NoAction;
   }
 
@@ -5046,16 +5040,24 @@ bool CallIRGenerator::getTemplateObjectF
 
   if (thisObject->is<PlainObject>()) {
     result.set(thisObject);
   }
 
   return true;
 }
 
+AttachDecision CallIRGenerator::tryAttachSelfHosted(HandleFunction calleeFunc) {
+  if (isOptimizableConstStringSplit(calleeFunc)) {
+    return AttachDecision::Deferred;
+  }
+
+  return AttachDecision::NoAction;
+}
+
 AttachDecision CallIRGenerator::tryAttachCallScripted(
     HandleFunction calleeFunc) {
   if (JitOptions.disableCacheIRCalls) {
     return AttachDecision::NoAction;
   }
 
   // Never attach optimized scripted call stubs for JSOP_FUNAPPLY.
   // MagicArguments may escape the frame through them.
@@ -5095,16 +5097,18 @@ AttachDecision CallIRGenerator::tryAttac
   }
 
   // Keep track of the function's |prototype| property in type
   // information, for use during Ion compilation.
   if (IsIonEnabled(cx_)) {
     EnsureTrackPropertyTypes(cx_, calleeFunc, NameToId(cx_->names().prototype));
   }
 
+  TRY_ATTACH(tryAttachSelfHosted(calleeFunc));
+
   RootedObject templateObj(cx_);
   bool skipAttach = false;
   if (isConstructing && isSpecialized &&
       !getTemplateObjectForScripted(calleeFunc, &templateObj, &skipAttach)) {
     cx_->clearPendingException();
     return AttachDecision::NoAction;
   }
   if (skipAttach) {
@@ -5454,58 +5458,66 @@ AttachDecision CallIRGenerator::tryAttac
   // Check for native-function optimizations.
   if (calleeFunc->isNative()) {
     return tryAttachCallNative(calleeFunc);
   }
 
   return AttachDecision::NoAction;
 }
 
-bool CallIRGenerator::isOptimizableConstStringSplit() {
+bool CallIRGenerator::isOptimizableConstStringSplit(HandleFunction calleeFunc) {
   // If we have not yet attached any stubs to this IC...
   if (!isFirstStub_) {
     return false;
   }
 
-  // And we have two arguments, both of which are strings...
-  if (argc_ != 2 || !args_[0].isString() || !args_[1].isString()) {
+  // And |this| is a string:
+  if (!thisval_.isString()) {
     return false;
   }
 
-  // And the strings are atoms...
-  if (!args_[0].toString()->isAtom() || !args_[1].toString()->isAtom()) {
+  // And we have one argument, which is a string...
+  if (argc_ != 1 || !args_[0].isString()) {
+    return false;
+  }
+
+  // And both strings are atoms...
+  if (!thisval_.toString()->isAtom() || !args_[0].toString()->isAtom()) {
     return false;
   }
 
   // And we are calling a function in the current realm...
-  RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
   if (calleeFunc->realm() != cx_->realm()) {
     return false;
   }
 
-  // Which is the String split intrinsic...
-  if (!calleeFunc->isNative() ||
-      calleeFunc->native() != js::intrinsic_StringSplitString) {
+  // Which is the String split self-hosted function
+  if (!IsSelfHostedFunctionWithName(calleeFunc, cx_->names().String_split)) {
     return false;
   }
 
   // Then this might be a call of the form:
   //  "literal list".split("literal separator")
   // If so, we can cache the result and avoid having to perform the operation
   // each time.
   return true;
 }
 
-AttachDecision CallIRGenerator::tryAttachConstStringSplit(HandleValue result) {
-  if (!isOptimizableConstStringSplit()) {
-    return AttachDecision::NoAction;
-  }
-
-  RootedString str(cx_, args_[0].toString());
-  RootedString sep(cx_, args_[1].toString());
+AttachDecision CallIRGenerator::tryAttachConstStringSplit(
+    HandleValue result, HandleFunction calleeFunc) {
+  if (JitOptions.disableCacheIRCalls) {
+    return AttachDecision::NoAction;
+  }
+
+  if (!isOptimizableConstStringSplit(calleeFunc)) {
+    return AttachDecision::NoAction;
+  }
+
+  RootedString str(cx_, thisval_.toString());
+  RootedString sep(cx_, args_[0].toString());
   RootedArrayObject resultObj(cx_, &result.toObject().as<ArrayObject>());
   uint32_t initLength = resultObj->getDenseInitializedLength();
   MOZ_ASSERT(initLength == resultObj->length(),
              "string-split result is a fully initialized array");
 
   // Copy the array before storing in stub.
   RootedArrayObject arrObj(cx_);
   arrObj = NewFullyAllocatedArrayTryReuseGroup(cx_, resultObj, initLength,
@@ -5530,60 +5542,59 @@ AttachDecision CallIRGenerator::tryAttac
         return AttachDecision::NoAction;
       }
       arrObj->initDenseElement(i, StringValue(str));
     }
   }
 
   Int32OperandId argcId(writer.setInputOperandId(0));
 
-  // Guard that callee is the |intrinsic_StringSplitString| native function.
+  // Guard that callee is the self-hosted String_split function
   ValOperandId calleeValId =
-      writer.loadArgumentFixedSlot(ArgumentKind::Callee, 2);
+      writer.loadArgumentFixedSlot(ArgumentKind::Callee, argc_);
   ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
-  writer.guardSpecificNativeFunction(calleeObjId, intrinsic_StringSplitString);
-
-  // Guard that the first argument is the expected string
-  ValOperandId strValId = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, 2);
+  writer.guardSpecificObject(calleeObjId, calleeFunc);
+
+  // Guard that |this| is the expected string
+  ValOperandId strValId =
+      writer.loadArgumentFixedSlot(ArgumentKind::This, argc_);
   StringOperandId strStringId = writer.guardIsString(strValId);
-  writer.guardSpecificAtom(strStringId, &str->asAtom());
-
-  // Guard that the second argument is the expected string
-  ValOperandId sepValId = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, 2);
+  FieldOffset strOffset = writer.guardSpecificAtom(strStringId, &str->asAtom());
+
+  // Guard that the argument is the expected separator
+  ValOperandId sepValId =
+      writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
   StringOperandId sepStringId = writer.guardIsString(sepValId);
-  writer.guardSpecificAtom(sepStringId, &sep->asAtom());
-
-  writer.callConstStringSplitResult(arrObj);
+  FieldOffset sepOffset = writer.guardSpecificAtom(sepStringId, &sep->asAtom());
+
+  FieldOffset templateOffset = writer.callConstStringSplitResult(arrObj);
 
   writer.typeMonitorResult();
   cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
 
+  writer.metaConstStringSplitData(strOffset, sepOffset, templateOffset);
+
   trackAttached("Const string split");
-
   return AttachDecision::Attach;
 }
 
 AttachDecision CallIRGenerator::tryAttachDeferredStub(HandleValue result) {
   AutoAssertNoPendingException aanpe(cx_);
 
   // Ensure that the opcode makes sense.
   MOZ_ASSERT(op_ == JSOP_CALL || op_ == JSOP_CALL_IGNORES_RV);
 
   // Ensure that the mode makes sense.
   MOZ_ASSERT(mode_ == ICState::Mode::Specialized);
 
-  // We currently only defer native functions.
   RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
-  MOZ_ASSERT(calleeFunc->isNative());
-
-  if (calleeFunc->native() == js::intrinsic_StringSplitString) {
-    return tryAttachConstStringSplit(result);
-  }
-
-  MOZ_ASSERT_UNREACHABLE("Unexpected deferred function");
+
+  TRY_ATTACH(tryAttachConstStringSplit(result, calleeFunc));
+
+  MOZ_ASSERT_UNREACHABLE("Unexpected deferred function failure");
   return AttachDecision::NoAction;
 }
 
 void CallIRGenerator::trackAttached(const char* name) {
 #ifdef JS_CACHEIR_SPEW
   if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
     sp.valueProperty("callee", callee_);
     sp.valueProperty("thisval", thisval_);
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -296,16 +296,17 @@ extern const uint32_t ArgLengths[];
   _(CallNumberToString, Id, Id)                                                \
   _(BooleanToString, Id, Id)                                                   \
   _(CallScriptedFunction, Id, Id, Byte)                                        \
   _(CallNativeFunction, Id, Id, Byte, IF_SIMULATOR(Field, Byte))               \
   _(CallClassHook, Id, Id, Byte, Field)                                        \
                                                                                \
   /* Meta ops generate no code, but contain data for BaselineInspector */      \
   _(MetaTwoByte, Byte, Field, Field)                                           \
+  _(MetaThreeByte, Byte, Field, Field, Field)                                  \
                                                                                \
   /* The *Result ops load a value into the cache's result register. */         \
   _(LoadFixedSlotResult, Id, Field)                                            \
   _(LoadDynamicSlotResult, Id, Field)                                          \
   _(LoadTypedObjectResult, Id, Byte, Byte, Field)                              \
   _(LoadDenseElementResult, Id, Id)                                            \
   _(LoadDenseElementHoleResult, Id, Id)                                        \
   _(CallGetSparseElementResult, Id, Id)                                        \
@@ -529,22 +530,22 @@ enum class AttachDecision {
   // for the deferred portion. This prevents arbitrary scripted code
   // run by the operation from interfering with the conditions being
   // checked.
   Deferred
 };
 
 // If the input expression evaluates to an AttachDecision other than NoAction,
 // return that AttachDecision. If it is NoAction, do nothing.
-#define TRY_ATTACH(expr)                      \
-  do {                                        \
-    AttachDecision result = expr;             \
-    if (result != AttachDecision::NoAction) { \
-      return result;                          \
-    }                                         \
+#define TRY_ATTACH(expr)                                    \
+  do {                                                      \
+    AttachDecision tryAttachTempResult_ = expr;             \
+    if (tryAttachTempResult_ != AttachDecision::NoAction) { \
+      return tryAttachTempResult_;                          \
+    }                                                       \
   } while (0)
 
 // Set of arguments supported by GetIndexOfArgument.
 // Support for Arg2 and up can be added easily, but is currently unneeded.
 enum class ArgumentKind : uint8_t { Callee, This, NewTarget, Arg0, Arg1 };
 
 // This function calculates the index of an argument based on the call flags.
 // addArgc is an out-parameter, indicating whether the value of argc should
@@ -628,16 +629,20 @@ void LoadShapeWrapperContents(MacroAssem
                               Label* failure);
 
 enum class MetaTwoByteKind : uint8_t {
   NativeTemplateObject,
   ScriptedTemplateObject,
   ClassTemplateObject,
 };
 
+enum class MetaThreeByteKind : uint8_t {
+  ConstStringSplitData,
+};
+
 #ifdef JS_SIMULATOR
 bool CallAnyNative(JSContext* cx, unsigned argc, Value* vp);
 #endif
 
 // Class to record CacheIR + some additional metadata for code generation.
 class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter {
   JSContext* cx_;
   CompactBufferWriter buffer_;
@@ -960,19 +965,19 @@ class MOZ_RAII CacheIRWriter : public JS
   void guardNotDOMProxy(ObjOperandId obj) {
     writeOpWithOperandId(CacheOp::GuardNotDOMProxy, obj);
   }
   FieldOffset guardSpecificObject(ObjOperandId obj, JSObject* expected) {
     assertSameCompartment(expected);
     writeOpWithOperandId(CacheOp::GuardSpecificObject, obj);
     return addStubField(uintptr_t(expected), StubField::Type::JSObject);
   }
-  void guardSpecificAtom(StringOperandId str, JSAtom* expected) {
+  FieldOffset guardSpecificAtom(StringOperandId str, JSAtom* expected) {
     writeOpWithOperandId(CacheOp::GuardSpecificAtom, str);
-    addStubField(uintptr_t(expected), StubField::Type::String);
+    return 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 guardSpecificInt32Immediate(
       Int32OperandId operand, int32_t expected,
       Assembler::Condition cond = Assembler::Equal) {
@@ -1424,16 +1429,24 @@ class MOZ_RAII CacheIRWriter : public JS
   }
   void metaClassTemplateObject(JSObject* templateObject,
                                FieldOffset classOffset) {
     writeOp(CacheOp::MetaTwoByte);
     buffer_.writeByte(uint32_t(MetaTwoByteKind::ClassTemplateObject));
     reuseStubField(classOffset);
     addStubField(uintptr_t(templateObject), StubField::Type::JSObject);
   }
+  void metaConstStringSplitData(FieldOffset strOffset, FieldOffset sepOffset,
+                                FieldOffset templateOffset) {
+    writeOp(CacheOp::MetaThreeByte);
+    buffer_.writeByte(uint32_t(MetaThreeByteKind::ConstStringSplitData));
+    reuseStubField(strOffset);
+    reuseStubField(sepOffset);
+    reuseStubField(templateOffset);
+  }
 
   void megamorphicLoadSlotResult(ObjOperandId obj, PropertyName* name,
                                  bool handleMissing) {
     writeOpWithOperandId(CacheOp::MegamorphicLoadSlotResult, obj);
     addStubField(uintptr_t(name), StubField::Type::String);
     buffer_.writeByte(uint32_t(handleMissing));
   }
   void megamorphicLoadSlotByValueResult(ObjOperandId obj, ValOperandId id,
@@ -1721,19 +1734,19 @@ class MOZ_RAII CacheIRWriter : public JS
     writeOperandId(rhs);
   }
   void callStringSplitResult(StringOperandId str, StringOperandId sep,
                              ObjectGroup* group) {
     writeOpWithOperandId(CacheOp::CallStringSplitResult, str);
     writeOperandId(sep);
     addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
   }
-  void callConstStringSplitResult(ArrayObject* resultTemplate) {
+  FieldOffset callConstStringSplitResult(ArrayObject* resultTemplate) {
     writeOp(CacheOp::CallConstStringSplitResult);
-    addStubField(uintptr_t(resultTemplate), StubField::Type::JSObject);
+    return addStubField(uintptr_t(resultTemplate), StubField::Type::JSObject);
   }
 
   void compareStringResult(uint32_t op, StringOperandId lhs,
                            StringOperandId rhs) {
     writeOpWithOperandId(CacheOp::CompareStringResult, lhs);
     writeOperandId(rhs);
     buffer_.writeByte(uint32_t(op));
   }
@@ -2379,35 +2392,37 @@ class MOZ_RAII CallIRGenerator : public 
 
   // Regular stubs
   AttachDecision tryAttachStringSplit();
   AttachDecision tryAttachArrayPush();
   AttachDecision tryAttachArrayJoin();
   AttachDecision tryAttachIsSuspendedGenerator();
   AttachDecision tryAttachFunCall();
   AttachDecision tryAttachFunApply();
+  AttachDecision tryAttachSelfHosted(HandleFunction calleeFunc);
   AttachDecision tryAttachCallScripted(HandleFunction calleeFunc);
   AttachDecision tryAttachSpecialCaseCallNative(HandleFunction calleeFunc);
   AttachDecision tryAttachCallNative(HandleFunction calleeFunc);
   AttachDecision tryAttachCallHook(HandleObject calleeObj);
 
   // Deferred stubs
-  AttachDecision tryAttachConstStringSplit(HandleValue result);
+  AttachDecision tryAttachConstStringSplit(HandleValue result,
+                                           HandleFunction calleeFunc);
 
   void trackAttached(const char* name);
 
  public:
   CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op,
                   ICState::Mode mode, uint32_t argc, HandleValue callee,
                   HandleValue thisval, HandleValue newTarget,
                   HandleValueArray args, bool isFirstStub);
 
   AttachDecision tryAttachStub();
 
-  bool isOptimizableConstStringSplit();
+  bool isOptimizableConstStringSplit(HandleFunction calleeFunc);
   AttachDecision tryAttachDeferredStub(HandleValue result);
 
   BaselineCacheIRStubKind cacheIRStubKind() const { return cacheIRStubKind_; }
 
   const PropertyTypeCheckInfo* typeCheckInfo() const { return &typeCheckInfo_; }
 };
 
 class MOZ_RAII CompareIRGenerator : public IRGenerator {
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -954,16 +954,18 @@ template GCPtr<JSFunction*>& CacheIRStub
 template GCPtr<JS::Symbol*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<JS::Value>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<jsid>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
 template GCPtr<Class*>& CacheIRStubInfo::getStubField<ICStub>(
     ICStub* stub, uint32_t offset) const;
+template GCPtr<ArrayObject*>& CacheIRStubInfo::getStubField<ICStub>(
+    ICStub* stub, uint32_t offset) const;
 
 template <typename T, typename V>
 static void InitGCPtr(uintptr_t* ptr, V val) {
   AsGCPtr<T>(ptr)->init(mozilla::BitwiseCast<T>(val));
 }
 
 void CacheIRWriter::copyStubData(uint8_t* dest) const {
   MOZ_ASSERT(!failed());
@@ -4378,8 +4380,18 @@ bool CacheIRCompiler::emitCallIsSuspende
 // This op generates no code. It is consumed by BaselineInspector.
 bool CacheIRCompiler::emitMetaTwoByte() {
   mozilla::Unused << reader.readByte();  // meta kind
   mozilla::Unused << reader.readByte();  // payload byte 1
   mozilla::Unused << reader.readByte();  // payload byte 2
 
   return true;
 }
+
+// This op generates no code. It is consumed by BaselineInspector.
+bool CacheIRCompiler::emitMetaThreeByte() {
+  mozilla::Unused << reader.readByte();  // meta kind
+  mozilla::Unused << reader.readByte();  // payload byte 1
+  mozilla::Unused << reader.readByte();  // payload byte 2
+  mozilla::Unused << reader.readByte();  // payload byte 3
+
+  return true;
+}
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -124,16 +124,17 @@ namespace jit {
   _(MegamorphicStoreSlot)                 \
   _(MegamorphicHasPropResult)             \
   _(CallObjectHasSparseElementResult)     \
   _(CallInt32ToString)                    \
   _(CallNumberToString)                   \
   _(BooleanToString)                      \
   _(CallIsSuspendedGeneratorResult)       \
   _(MetaTwoByte)                          \
+  _(MetaThreeByte)                        \
   _(WrapResult)
 
 // [SMDDOC] CacheIR Value Representation and Tracking
 //
 // While compiling an IC stub the CacheIR compiler needs to keep track of the
 // physical location for each logical piece of data we care about, as well as
 // ensure that in the case of a stub failing, we are able to restore the input
 // state so that a subsequent stub can attempt to provide a value.
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -384,16 +384,17 @@
   MACRO(startTimestamp, startTimestamp, "startTimestamp")                      \
   MACRO(state, state, "state")                                                 \
   MACRO(static, static_, "static")                                             \
   MACRO(status, status, "status")                                              \
   MACRO(std_Function_apply, std_Function_apply, "std_Function_apply")          \
   MACRO(sticky, sticky, "sticky")                                              \
   MACRO(StringIterator, StringIterator, "String Iterator")                     \
   MACRO(strings, strings, "strings")                                           \
+  MACRO(String_split, String_split, "String_split")                            \
   MACRO(StructType, StructType, "StructType")                                  \
   MACRO(style, style, "style")                                                 \
   MACRO(super, super, "super")                                                 \
   MACRO(switch, switch_, "switch")                                             \
   MACRO(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]")         \
   MACRO(target, target, "target")                                              \
   MACRO(test, test, "test")                                                    \
   MACRO(then, then, "then")                                                    \