Bug 1319087 - Introduce CacheIR for TypeMonitorResult/ReturnFromIC. r=jandem
authorTom Schuster <evilpies@gmail.com>
Fri, 25 Nov 2016 21:15:32 +0100
changeset 324321 e7aad63193d725ddf8a690691969f4de17520f0d
parent 324320 31ecbfd5aa7b8ae4b78f8cb238b15f36c5542a98
child 324322 8aee096b3c4b3780b5d1a61d814bfe383664e285
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersjandem
bugs1319087
milestone53.0a1
Bug 1319087 - Introduce CacheIR for TypeMonitorResult/ReturnFromIC. r=jandem
js/src/jit/BaselineCacheIR.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -507,24 +507,16 @@ class MOZ_RAII BaselineCacheIRCompiler :
         }
 
         if (!failurePaths.append(Move(newFailure)))
             return false;
 
         *failure = &failurePaths.back();
         return true;
     }
-    void emitEnterTypeMonitorIC() {
-        allocator.discardStack(masm);
-        EmitEnterTypeMonitorIC(masm);
-    }
-    void emitReturnFromIC() {
-        allocator.discardStack(masm);
-        EmitReturnFromIC(masm);
-    }
 };
 
 void
 BaselineCacheIRCompiler::enterStubFrame(MacroAssembler& masm, Register scratch)
 {
     if (engine_ == ICStubEngine::Baseline) {
         EmitBaselineEnterStubFrame(masm, scratch);
 #ifdef DEBUG
@@ -599,16 +591,18 @@ BaselineCacheIRCompiler::compile()
 
           default:
             MOZ_CRASH("Invalid op");
         }
 
         allocator.nextOp();
     } while (reader.more());
 
+    masm.assumeUnreachable("Should have returned from IC");
+
     // Done emitting the main IC code. Now emit the failure paths.
     for (size_t i = 0; i < failurePaths.length(); i++) {
         emitFailurePath(i);
         EmitStubGuardFailure(masm);
     }
 
     Linker linker(masm);
     AutoFlushICache afc("getStubCode");
@@ -1052,31 +1046,29 @@ BaselineCacheIRCompiler::emitGuardAndLoa
 bool
 BaselineCacheIRCompiler::emitLoadFixedSlotResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
 
     masm.load32(stubAddress(reader.stubOffset()), scratch);
     masm.loadValue(BaseIndex(obj, scratch, TimesOne), R0);
-    emitEnterTypeMonitorIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadDynamicSlotResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
 
     // We're about to return, so it's safe to clobber obj now.
     masm.load32(stubAddress(reader.stubOffset()), scratch);
     masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj);
     masm.loadValue(BaseIndex(obj, scratch, TimesOne), R0);
-    emitEnterTypeMonitorIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitCallScriptedGetterResult()
 {
     MOZ_ASSERT(engine_ == ICStubEngine::Baseline);
 
@@ -1137,18 +1129,16 @@ BaselineCacheIRCompiler::emitCallScripte
         masm.loadPtr(Address(code, JitCode::offsetOfCode()), code);
         masm.movePtr(ImmWord(0), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
     masm.callJit(code);
 
     leaveStubFrame(masm, true);
-
-    emitEnterTypeMonitorIC();
     return true;
 }
 
 typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
 static const VMFunction DoCallNativeGetterInfo =
     FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter");
 
 bool
@@ -1175,38 +1165,29 @@ BaselineCacheIRCompiler::emitCallNativeG
 
     masm.Push(obj);
     masm.Push(scratch);
 
     if (!callVM(masm, DoCallNativeGetterInfo))
         return false;
 
     leaveStubFrame(masm);
-
-    emitEnterTypeMonitorIC();
     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()));
     masm.load32(fieldOffset, scratch);
     masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0);
-
-    if (fieldType == JSVAL_TYPE_OBJECT)
-        emitEnterTypeMonitorIC();
-    else
-        emitReturnFromIC();
-
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects()
 {
     FailurePath* failure;
     if (!addFailurePath(&failure))
@@ -1229,28 +1210,22 @@ BaselineCacheIRCompiler::emitLoadTypedOb
 
     // Get the object's data pointer.
     LoadTypedThingData(masm, layout, obj, scratch1);
 
     // Get the address being written to.
     masm.load32(fieldOffset, scratch2);
     masm.addPtr(scratch2, scratch1);
 
-    // Only monitor the result if the type produced by this stub might vary.
-    bool monitorLoad;
     if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
         Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
-        monitorLoad = type == Scalar::Uint32;
-
         masm.loadFromTypedArray(type, Address(scratch1, 0), R0, /* allowDouble = */ true,
                                 scratch2, nullptr);
     } else {
         ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
-        monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
-
         switch (type) {
           case ReferenceTypeDescr::TYPE_ANY:
             masm.loadValue(Address(scratch1, 0), R0);
             break;
 
           case ReferenceTypeDescr::TYPE_OBJECT: {
             Label notNull, done;
             masm.loadPtr(Address(scratch1, 0), scratch1);
@@ -1268,32 +1243,23 @@ BaselineCacheIRCompiler::emitLoadTypedOb
             masm.tagValue(JSVAL_TYPE_STRING, scratch1, R0);
             break;
 
           default:
             MOZ_CRASH("Invalid ReferenceTypeDescr");
         }
     }
 
-    if (monitorLoad)
-        emitEnterTypeMonitorIC();
-    else
-        emitReturnFromIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadUndefinedResult()
 {
     masm.moveValue(UndefinedValue(), R0);
-
-    // Normally for this op, the result would have to be monitored by TI.
-    // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
-    // that undefined is already registered with the type-set, this can be avoided.
-    emitReturnFromIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
@@ -1303,33 +1269,25 @@ BaselineCacheIRCompiler::emitLoadInt32Ar
         return false;
 
     masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
     masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch);
 
     // Guard length fits in an int32.
     masm.branchTest32(Assembler::Signed, scratch, scratch, failure->label());
     masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
-
-    // The int32 type was monitored when attaching the stub, so we can
-    // just return.
-    emitReturnFromIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg());
     masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0);
-
-    // The int32 type was monitored when attaching the stub, so we can
-    // just return.
-    emitReturnFromIC();
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
@@ -1346,17 +1304,32 @@ BaselineCacheIRCompiler::emitLoadArgumen
                       scratch,
                       Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT),
                       failure->label());
 
     // Shift out arguments length and return it. No need to type monitor
     // because this stub always returns int32.
     masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratch);
     masm.tagValue(JSVAL_TYPE_INT32, scratch, R0);
-    emitReturnFromIC();
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitTypeMonitorResult()
+{
+    allocator.discardStack(masm);
+    EmitEnterTypeMonitorIC(masm);
+    return true;
+}
+
+bool
+BaselineCacheIRCompiler::emitReturnFromIC()
+{
+    allocator.discardStack(masm);
+    EmitReturnFromIC(masm);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitLoadObject()
 {
     Register reg = allocator.defineRegister(masm, reader.objOperandId());
     masm.loadPtr(stubAddress(reader.stubOffset()), reg);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -241,16 +241,31 @@ EmitReadSlotResult(CacheIRWriter& writer
         EmitLoadSlotResult(writer, holderId, &holder->as<NativeObject>(), shape);
     } else {
         MOZ_ASSERT(!holderId.valid());
         writer.loadUndefinedResult();
     }
 }
 
 static void
+EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape)
+{
+    // Slot access.
+    if (holder) {
+        MOZ_ASSERT(shape);
+        writer.typeMonitorResult();
+    } else {
+        // Normally for this op, the result would have to be monitored by TI.
+        // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
+        // that undefined is already registered with the type-set, this can be avoided.
+        writer.returnFromIC();
+    }
+}
+
+static void
 EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
                      Shape* shape, ObjOperandId objId)
 {
     Maybe<ObjOperandId> expandoId;
     TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
 
     if (obj != holder) {
         GeneratePrototypeGuards(writer, obj, holder, objId);
@@ -259,24 +274,26 @@ EmitCallGetterResult(CacheIRWriter& writ
         ObjOperandId holderId = writer.loadObject(holder);
         writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
     }
 
     if (IsCacheableGetPropCallNative(obj, holder, shape)) {
         JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
         MOZ_ASSERT(target->isNative());
         writer.callNativeGetterResult(objId, target);
+        writer.typeMonitorResult();
         return;
     }
 
     MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
 
     JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
     MOZ_ASSERT(target->hasJITCode());
     writer.callScriptedGetterResult(objId, target);
+    writer.typeMonitorResult();
 }
 
 bool
 GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
 {
     MOZ_ASSERT(!emitted_);
 
     RootedShape shape(cx_);
@@ -298,16 +315,17 @@ GetPropIRGenerator::tryAttachNative(Cach
                 // 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);
         break;
       case CanAttachCallGetter:
         EmitCallGetterResult(writer, obj, holder, shape, objId);
         break;
       default:
         MOZ_CRASH("Bad NativeGetPropCacheability");
     }
 
@@ -376,16 +394,21 @@ GetPropIRGenerator::tryAttachUnboxed(Cac
         return true;
 
     if (!cx_->runtime()->jitSupportsFloatingPoint)
         return true;
 
     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();
+
     emitted_ = true;
     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
 {
@@ -400,16 +423,17 @@ GetPropIRGenerator::tryAttachUnboxedExpa
 
     Shape* shape = expando->lookup(cx_, NameToId(name_));
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return true;
 
     emitted_ = true;
 
     EmitReadSlotResult(writer, obj, obj, shape, objId);
+    EmitReadSlotReturn(writer, obj, obj, shape);
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
 {
     MOZ_ASSERT(!emitted_);
 
@@ -437,16 +461,32 @@ GetPropIRGenerator::tryAttachTypedObject
     TypedThingLayout layout = GetTypedThingLayout(shape->getObjectClass());
 
     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
     uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
 
     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);
+        monitorLoad = type == Scalar::Uint32;
+    } else {
+        ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
+        monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
+    }
+
+    if (monitorLoad)
+        writer.typeMonitorResult();
+    else
+        writer.returnFromIC();
+
     emitted_ = true;
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId)
 {
     MOZ_ASSERT(!emitted_);
@@ -457,35 +497,38 @@ GetPropIRGenerator::tryAttachObjectLengt
     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 true;
 
         writer.guardClass(objId, GuardClassKind::Array);
         writer.loadInt32ArrayLengthResult(objId);
+        writer.returnFromIC();
         emitted_ = true;
         return true;
     }
 
     if (obj->is<UnboxedArrayObject>()) {
         writer.guardClass(objId, GuardClassKind::UnboxedArray);
         writer.loadUnboxedArrayLengthResult(objId);
+        writer.returnFromIC();
         emitted_ = true;
         return true;
     }
 
     if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
         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();
         emitted_ = true;
         return true;
     }
 
     return true;
 }
 
 bool
@@ -512,16 +555,17 @@ GetPropIRGenerator::tryAttachModuleNames
 
     emitted_ = true;
 
     // Check for the specific namespace object.
     writer.guardSpecificObject(objId, ns);
 
     ObjOperandId envId = writer.loadObject(env);
     EmitLoadSlotResult(writer, envId, env, shape);
+    writer.typeMonitorResult();
     return true;
 }
 
 bool
 GetPropIRGenerator::tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId)
 {
     MOZ_ASSERT(!emitted_);
 
@@ -560,12 +604,13 @@ GetPropIRGenerator::tryAttachPrimitive(C
     if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
         return true;
 
     writer.guardType(valId, primitiveType);
 
     ObjOperandId protoId = writer.loadObject(proto);
     writer.guardShape(protoId, proto->lastProperty());
     EmitLoadSlotResult(writer, protoId, proto, shape);
+    writer.typeMonitorResult();
 
     emitted_ = true;
     return true;
 }
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -97,17 +97,20 @@ class ObjOperandId : public OperandId
     _(LoadDynamicSlotResult)              \
     _(LoadUnboxedPropertyResult)          \
     _(LoadTypedObjectResult)              \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
-    _(LoadUndefinedResult)
+    _(LoadUndefinedResult)                \
+                                          \
+    _(TypeMonitorResult)                  \
+    _(ReturnFromIC)
 
 enum class CacheOp {
 #define DEFINE_OP(op) op,
     CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
 };
 
 struct StubField {
@@ -342,16 +345,23 @@ class MOZ_RAII CacheIRWriter
     void callScriptedGetterResult(ObjOperandId obj, JSFunction* getter) {
         writeOpWithOperandId(CacheOp::CallScriptedGetterResult, obj);
         addStubWord(uintptr_t(getter), StubField::GCType::JSObject);
     }
     void callNativeGetterResult(ObjOperandId obj, JSFunction* getter) {
         writeOpWithOperandId(CacheOp::CallNativeGetterResult, obj);
         addStubWord(uintptr_t(getter), StubField::GCType::JSObject);
     }
+
+    void typeMonitorResult() {
+        writeOp(CacheOp::TypeMonitorResult);
+    }
+    void returnFromIC() {
+        writeOp(CacheOp::ReturnFromIC);
+    }
 };
 
 class CacheIRStubInfo;
 
 // Helper class for reading CacheIR bytecode.
 class MOZ_RAII CacheIRReader
 {
     CompactBufferReader buffer_;