Bug 1322091 part 1 - Port Baseline string GETELEM stub to CacheIR. r=evilpie
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 09 Dec 2016 12:19:51 -1000
changeset 325612 16720e74a36fed6a0df2843d83d72a9141ea0368
parent 325611 fb400cfedafabeba3530e1c41cd71524c650ad31
child 325613 b3249dd222cabf22cdf1f10a06d442ea5118db53
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersevilpie
bugs1322091
milestone53.0a1
Bug 1322091 part 1 - Port Baseline string GETELEM stub to CacheIR. r=evilpie
js/src/jit/BaselineCacheIR.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
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -940,16 +940,27 @@ BaselineCacheIRCompiler::emitGuardIsSymb
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardIsInt32()
+{
+    ValueOperand input = allocator.useValueRegister(masm, reader.valOperandId());
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+    masm.branchTestInt32(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;
@@ -1556,20 +1567,46 @@ BaselineCacheIRCompiler::emitLoadArgumen
     masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadStringLengthResult()
 {
     Register str = allocator.useRegister(masm, reader.stringOperandId());
+    masm.loadStringLength(str, R0.scratchReg());
+    masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitLoadStringCharResult()
+{
+    Register str = allocator.useRegister(masm, reader.stringOperandId());
+    Register index = allocator.useRegister(masm, reader.int32OperandId());
     AutoScratchRegister scratch(allocator, masm);
 
-    masm.loadStringLength(str, R0.scratchReg());
-    masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.branchIfRope(str, failure->label());
+
+    // Bounds check, load string char.
+    masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()),
+                  index, failure->label());
+    masm.loadStringChar(str, index, scratch);
+
+    // Load StaticString for this char.
+    masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
+                  failure->label());
+    masm.movePtr(ImmPtr(&cx_->staticStrings().unitStaticTable), R0.scratchReg());
+    masm.loadPtr(BaseIndex(R0.scratchReg(), scratch, ScalePointer), R0.scratchReg());
+
+    masm.tagValue(JSVAL_TYPE_STRING, R0.scratchReg(), R0);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitTypeMonitorResult()
 {
     allocator.discardStack(masm);
     EmitEnterTypeMonitorIC(masm);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -946,33 +946,16 @@ IsNativeOrUnboxedDenseElementAccess(Hand
         return true;
     return false;
 }
 
 static bool
 TryAttachGetElemStub(JSContext* cx, JSScript* script, jsbytecode* pc, ICGetElem_Fallback* stub,
                      HandleValue lhs, HandleValue rhs, HandleValue res, bool* attached)
 {
-    // Check for String[i] => Char accesses.
-    if (lhs.isString() && rhs.isInt32() && res.isString() &&
-        !stub->hasStub(ICStub::GetElem_String))
-    {
-        // NoSuchMethod handling doesn't apply to string targets.
-
-        JitSpew(JitSpew_BaselineIC, "  Generating GetElem(String[Int32]) stub");
-        ICGetElem_String::Compiler compiler(cx);
-        ICStub* stringStub = compiler.getStub(compiler.getStubSpace(script));
-        if (!stringStub)
-            return false;
-
-        stub->addNewStub(stringStub);
-        *attached = true;
-        return true;
-    }
-
     if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() &&
         !ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic))
     {
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(MagicArgs[Int32]) stub");
         ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
                                                ICGetElem_Arguments::Magic);
         ICStub* argsStub = compiler.getStub(compiler.getStubSpace(script));
         if (!argsStub)
@@ -1188,66 +1171,16 @@ ICGetElem_Fallback::Compiler::generateSt
     masm.pushValue(R0);
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoGetElemFallbackInfo, masm);
 }
 
 //
-// GetElem_String
-//
-
-bool
-ICGetElem_String::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    masm.branchTestString(Assembler::NotEqual, R0, &failure);
-    masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratchReg = regs.takeAny();
-
-    // Unbox string in R0.
-    Register str = masm.extractString(R0, ExtractTemp0);
-
-    // Check for non-linear strings.
-    masm.branchIfRope(str, &failure);
-
-    // Unbox key.
-    Register key = masm.extractInt32(R1, ExtractTemp1);
-
-    // Bounds check.
-    masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()),
-                  key, &failure);
-
-    // Get char code.
-    masm.loadStringChar(str, key, scratchReg);
-
-    // Check if char code >= UNIT_STATIC_LIMIT.
-    masm.branch32(Assembler::AboveOrEqual, scratchReg, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
-                  &failure);
-
-    // Load static string.
-    masm.movePtr(ImmPtr(&cx->staticStrings().unitStaticTable), str);
-    masm.loadPtr(BaseIndex(str, scratchReg, ScalePointer), str);
-
-    // Return.
-    masm.tagValue(JSVAL_TYPE_STRING, str, R0);
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
-//
 // GetElem_Dense
 //
 
 bool
 ICGetElem_Dense::Compiler::generateStubCode(MacroAssembler& masm)
 {
     MOZ_ASSERT(engine_ == Engine::Baseline);
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -414,39 +414,16 @@ class ICGetElem_Fallback : public ICMoni
                 return nullptr;
             if (!stub->initMonitoringChain(cx, space, engine_))
                 return nullptr;
             return stub;
         }
     };
 };
 
-class ICGetElem_String : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICGetElem_String(JitCode* stubCode)
-      : ICStub(ICStub::GetElem_String, stubCode) {}
-
-  public:
-    // Compiler for this stub kind.
-    class Compiler : public ICStubCompiler {
-      protected:
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      public:
-        explicit Compiler(JSContext* cx)
-          : ICStubCompiler(cx, ICStub::GetElem_String, Engine::Baseline) {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            return newStub<ICGetElem_String>(space, getStubCode());
-        }
-    };
-};
-
 class ICGetElem_Dense : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
     GCPtrShape shape_;
 
     ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape);
 
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -44,17 +44,16 @@ namespace jit {
     _(Call_ClassHook)                            \
     _(Call_ScriptedApplyArray)                   \
     _(Call_ScriptedApplyArguments)               \
     _(Call_ScriptedFunCall)                      \
     _(Call_StringSplit)                          \
     _(Call_IsSuspendedStarGenerator)             \
                                                  \
     _(GetElem_Fallback)                          \
-    _(GetElem_String)                            \
     _(GetElem_Dense)                             \
     _(GetElem_UnboxedArray)                      \
     _(GetElem_TypedArray)                        \
     _(GetElem_Arguments)                         \
                                                  \
     _(SetElem_Fallback)                          \
     _(SetElem_DenseOrUnboxedArray)               \
     _(SetElem_DenseOrUnboxedArrayAdd)            \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -1015,17 +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_String:
           case ICStub::GetElem_Dense:
           case ICStub::GetElem_TypedArray:
           case ICStub::GetElem_UnboxedArray:
             stubType = MIRType::Object;
             break;
 
           case ICStub::CacheIR_Monitored:
             stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -91,16 +91,23 @@ GetPropIRGenerator::tryAttachStub()
 
     if (nameOrSymbol) {
         if (tryAttachPrimitive(valId, id))
             return true;
         if (tryAttachStringLength(valId, id))
             return true;
         if (tryAttachMagicArguments(valId, id))
             return true;
+        return false;
+    }
+
+    if (idVal_.isInt32()) {
+        if (tryAttachStringChar(valId, getElemKeyValueId()))
+            return true;
+        return false;
     }
 
     return false;
 }
 
 static bool
 IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
                       jsbytecode* pc)
@@ -770,16 +777,40 @@ GetPropIRGenerator::tryAttachStringLengt
     StringOperandId strId = writer.guardIsString(valId);
     maybeEmitIdGuard(id);
     writer.loadStringLengthResult(strId);
     writer.returnFromIC();
     return true;
 }
 
 bool
+GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
+{
+    MOZ_ASSERT(idVal_.isInt32());
+
+    if (!val_.isString())
+        return false;
+
+    JSString* str = val_.toString();
+    int32_t index = idVal_.toInt32();
+    if (size_t(index) >= str->length() ||
+        !str->isLinear() ||
+        str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
+    {
+        return false;
+    }
+
+    StringOperandId strId = writer.guardIsString(valId);
+    Int32OperandId int32IndexId = writer.guardIsInt32(indexId);
+    writer.loadStringCharResult(strId, int32IndexId);
+    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;
 
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -88,44 +88,55 @@ class StringOperandId : public OperandId
 
 class SymbolOperandId : public OperandId
 {
   public:
     SymbolOperandId() = default;
     explicit SymbolOperandId(uint16_t id) : OperandId(id) {}
 };
 
+class Int32OperandId : public OperandId
+{
+  public:
+    Int32OperandId() = default;
+    explicit Int32OperandId(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)
     {}
+    MOZ_IMPLICIT TypedOperandId(Int32OperandId id)
+      : OperandId(id.id()), type_(JSVAL_TYPE_INT32)
+    {}
 
     JSValueType type() const { return type_; }
 };
 
 enum class CacheKind : uint8_t
 {
     GetProp,
     GetElem,
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
     _(GuardIsString)                      \
     _(GuardIsSymbol)                      \
+    _(GuardIsInt32)                       \
     _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         \
     _(GuardIsProxy)                       \
     _(GuardNotDOMProxy)                   \
     _(GuardSpecificObject)                \
@@ -146,16 +157,17 @@ enum class CacheKind : uint8_t
     /* The *Result ops load a value into the cache's result register. */ \
     _(LoadFixedSlotResult)                \
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectLengthResult)    \
+    _(LoadStringCharResult)               \
     _(LoadStringLengthResult)             \
     _(LoadFrameCalleeResult)              \
     _(LoadFrameNumActualArgsResult)       \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
     _(CallProxyGetResult)                 \
     _(CallProxyGetByValueResult)          \
     _(LoadUndefinedResult)                \
@@ -363,16 +375,20 @@ class MOZ_RAII CacheIRWriter : public JS
     StringOperandId guardIsString(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsString, val);
         return StringOperandId(val.id());
     }
     SymbolOperandId guardIsSymbol(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsSymbol, val);
         return SymbolOperandId(val.id());
     }
+    Int32OperandId guardIsInt32(ValOperandId val) {
+        writeOpWithOperandId(CacheOp::GuardIsInt32, val);
+        return Int32OperandId(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);
@@ -502,16 +518,20 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOpWithOperandId(CacheOp::LoadUnboxedArrayLengthResult, obj);
     }
     void loadArgumentsObjectLengthResult(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::LoadArgumentsObjectLengthResult, obj);
     }
     void loadStringLengthResult(StringOperandId str) {
         writeOpWithOperandId(CacheOp::LoadStringLengthResult, str);
     }
+    void loadStringCharResult(StringOperandId str, Int32OperandId index) {
+        writeOpWithOperandId(CacheOp::LoadStringCharResult, str);
+        writeOperandId(index);
+    }
     void callScriptedGetterResult(ObjOperandId obj, JSFunction* getter) {
         writeOpWithOperandId(CacheOp::CallScriptedGetterResult, obj);
         addStubField(uintptr_t(getter), StubField::Type::JSObject);
     }
     void callNativeGetterResult(ObjOperandId obj, JSFunction* getter) {
         writeOpWithOperandId(CacheOp::CallNativeGetterResult, obj);
         addStubField(uintptr_t(getter), StubField::Type::JSObject);
     }
@@ -556,16 +576,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()); }
+    Int32OperandId int32OperandId() { return Int32OperandId(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()); }
 
@@ -618,16 +639,17 @@ class MOZ_RAII GetPropIRGenerator
     bool tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id);
 
     bool tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id);
     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 tryAttachStringChar(ValOperandId valId, ValOperandId indexId);
     bool tryAttachStringLength(ValOperandId valId, HandleId id);
     bool tryAttachMagicArguments(ValOperandId valId, HandleId id);
 
     ValOperandId getElemKeyValueId() const {
         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
         return ValOperandId(1);
     }