Bug 1322091 part 1 - Port Baseline string GETELEM stub to CacheIR. r=evilpie
--- 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);
}