Bug 1344469 - Part 5 - CacheIR HasOwn in Ion. r=jandem
authorTom Schuster <evilpies@gmail.com>
Thu, 13 Apr 2017 22:17:57 +0200
changeset 352836 a9d036ef05e83abbf6aa6370a4ef36bbc4d01706
parent 352835 5e1a2ab034aee990e3a13c84eabb4d7cfcfc8791
child 352837 56e3c896e6cc81a36f613c3e64e066695d92718f
push id89167
push userevilpies@gmail.com
push dateThu, 13 Apr 2017 20:18:48 +0000
treeherdermozilla-inbound@56e3c896e6cc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1344469
milestone55.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 1344469 - Part 5 - CacheIR HasOwn in Ion. r=jandem
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/IonIC.cpp
js/src/jit/IonIC.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1621,20 +1621,22 @@ CacheIRCompiler::emitLoadUndefinedResult
 
 bool
 CacheIRCompiler::emitLoadBooleanResult()
 {
     AutoOutputRegister output(*this);
     if (output.hasValue()) {
         Value val = BooleanValue(reader.readBool());
         masm.moveValue(val, output.valueReg());
+    } else {
+        MOZ_ASSERT(output.type() == JSVAL_TYPE_BOOLEAN);
+        bool b = reader.readBool();
+        masm.movePtr(ImmWord(b), output.typedReg().gpr());
     }
-    else {
-        MOZ_CRASH("NYI: Typed LoadBooleanResult");
-    }
+
     return true;
 }
 
 static void
 EmitStoreResult(MacroAssembler& masm, Register reg, JSValueType type,
                 const AutoOutputRegister& output)
 {
     if (output.hasValue()) {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -231,16 +231,21 @@ typedef bool (*IonSetPropertyICFn)(JSCon
 static const VMFunction IonSetPropertyICInfo =
     FunctionInfo<IonSetPropertyICFn>(IonSetPropertyIC::update, "IonSetPropertyIC::update");
 
 typedef bool (*IonGetNameICFn)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
                                MutableHandleValue);
 static const VMFunction IonGetNameICInfo =
     FunctionInfo<IonGetNameICFn>(IonGetNameIC::update, "IonGetNameIC::update");
 
+typedef bool (*IonHasOwnICFn)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, HandleValue,
+                              int32_t*);
+static const VMFunction IonHasOwnICInfo =
+    FunctionInfo<IonHasOwnICFn>(IonHasOwnIC::update, "IonHasOwnIC::update");
+
 void
 CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
 {
     LInstruction* lir = ool->lir();
     size_t cacheIndex = ool->cacheIndex();
     size_t cacheInfoIndex = ool->cacheInfoIndex();
 
     DataPtr<IonIC> ic(this, cacheIndex);
@@ -301,16 +306,34 @@ CodeGenerator::visitOutOfLineICFallback(
         StoreValueTo(getNameIC->output()).generate(this);
         restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
 
         masm.jump(ool->rejoin());
         return;
       }
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
+      case CacheKind::HasOwn: {
+        IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
+
+        saveLive(lir);
+
+        pushArg(hasOwnIC->id());
+        pushArg(hasOwnIC->value());
+        icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
+        pushArg(ImmGCPtr(gen->info().script()));
+
+        callVM(IonHasOwnICInfo, lir);
+
+        StoreRegisterTo(hasOwnIC->output()).generate(this);
+        restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
+
+        masm.jump(ool->rejoin());
+        return;
+      }
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
 {
     return &templateObj_->as<StringObject>();
@@ -10408,16 +10431,30 @@ CodeGenerator::visitBindNameIC(OutOfLine
     pushArg(ImmGCPtr(gen->info().script()));
     callVM(BindNameIC::UpdateInfo, lir);
     StoreRegisterTo(ic->outputReg()).generate(this);
     restoreLiveIgnore(lir, StoreRegisterTo(ic->outputReg()).clobbered());
 
     masm.jump(ool->rejoin());
 }
 
+void
+CodeGenerator::visitHasOwnCache(LHasOwnCache* ins)
+{
+    LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
+    TypedOrValueRegister value =
+        toConstantOrRegister(ins, LHasOwnCache::Value, ins->mir()->value()->type()).reg();
+    TypedOrValueRegister id =
+        toConstantOrRegister(ins, LHasOwnCache::Id, ins->mir()->idval()->type()).reg();
+    Register output = ToRegister(ins->output());
+
+    IonHasOwnIC cache(liveRegs, value, id, output);
+    addIC(ins, allocateIC(cache));
+}
+
 typedef bool (*SetPropertyFn)(JSContext*, HandleObject,
                               HandlePropertyName, const HandleValue, bool, jsbytecode*);
 static const VMFunction SetPropertyInfo = FunctionInfo<SetPropertyFn>(SetProperty, "SetProperty");
 
 void
 CodeGenerator::visitCallSetProperty(LCallSetProperty* ins)
 {
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LCallSetProperty::Value));
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -417,16 +417,17 @@ class CodeGenerator final : public CodeG
     void visitOutOfLineICFallback(OutOfLineICFallback* ool);
 
     void visitGetPropertyCacheV(LGetPropertyCacheV* ins);
     void visitGetPropertyCacheT(LGetPropertyCacheT* ins);
     void visitBindNameCache(LBindNameCache* ins);
     void visitCallSetProperty(LInstruction* ins);
     void visitSetPropertyCache(LSetPropertyCache* ins);
     void visitGetNameCache(LGetNameCache* ins);
+    void visitHasOwnCache(LHasOwnCache* ins);
 
     void visitBindNameIC(OutOfLineUpdateCache* ool, DataPtr<BindNameIC>& ic);
 
     void visitAssertRangeI(LAssertRangeI* ins);
     void visitAssertRangeD(LAssertRangeD* ins);
     void visitAssertRangeF(LAssertRangeF* ins);
     void visitAssertRangeV(LAssertRangeV* ins);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -653,16 +653,17 @@ IonBuilder::analyzeNewLoopTypes(const CF
               case JSOP_LE:
               case JSOP_GT:
               case JSOP_GE:
               case JSOP_NOT:
               case JSOP_STRICTEQ:
               case JSOP_STRICTNE:
               case JSOP_IN:
               case JSOP_INSTANCEOF:
+              case JSOP_HASOWN:
                 type = MIRType::Boolean;
                 break;
               case JSOP_DOUBLE:
                 type = MIRType::Double;
                 break;
               case JSOP_STRING:
               case JSOP_TOSTRING:
               case JSOP_TYPEOF:
@@ -2263,16 +2264,19 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_isnoiter();
 
       case JSOP_ENDITER:
         return jsop_iterend();
 
       case JSOP_IN:
         return jsop_in();
 
+      case JSOP_HASOWN:
+        return jsop_hasown();
+
       case JSOP_SETRVAL:
         MOZ_ASSERT(!script()->noScriptRval());
         current->setSlot(info().returnValueSlot(), current->pop());
         return Ok();
 
       case JSOP_INSTANCEOF:
         return jsop_instanceof();
 
@@ -12687,16 +12691,30 @@ IonBuilder::inTryFold(bool* emitted, MDe
     *emitted = true;
 
     pushConstant(BooleanValue(false));
     obj->setImplicitlyUsedUnchecked();
     id->setImplicitlyUsedUnchecked();
     return Ok();
 }
 
+AbortReasonOr<Ok>
+IonBuilder::jsop_hasown()
+{
+    MDefinition* obj = convertUnboxedObjects(current->pop());
+    MDefinition* id = current->pop();
+
+    MHasOwnCache* ins = MHasOwnCache::New(alloc(), obj, id);
+    current->add(ins);
+    current->push(ins);
+
+    MOZ_TRY(resumeAfter(ins));
+    return Ok();
+}
+
 AbortReasonOr<bool>
 IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* onProto)
 {
     MOZ_ASSERT(protoObject);
 
     while (true) {
         if (!alloc().ensureBallast())
             return abort(AbortReason::Alloc);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -573,16 +573,17 @@ class IonBuilder
     AbortReasonOr<Ok> jsop_toasyncgen();
     AbortReasonOr<Ok> jsop_toasynciter();
     AbortReasonOr<Ok> jsop_toid();
     AbortReasonOr<Ok> jsop_iter(uint8_t flags);
     AbortReasonOr<Ok> jsop_itermore();
     AbortReasonOr<Ok> jsop_isnoiter();
     AbortReasonOr<Ok> jsop_iterend();
     AbortReasonOr<Ok> jsop_in();
+    AbortReasonOr<Ok> jsop_hasown();
     AbortReasonOr<Ok> jsop_instanceof();
     AbortReasonOr<Ok> jsop_getaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_setaliasedvar(EnvironmentCoordinate ec);
     AbortReasonOr<Ok> jsop_debugger();
     AbortReasonOr<Ok> jsop_newtarget();
     AbortReasonOr<Ok> jsop_checkisobj(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkiscallable(uint8_t kind);
     AbortReasonOr<Ok> jsop_checkobjcoercible();
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -423,16 +423,30 @@ IonCacheIRCompiler::init()
         outputUnchecked_.emplace(output);
 
         MOZ_ASSERT(numInputs == 1);
         allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
         break;
       }
       case CacheKind::In:
         MOZ_CRASH("Invalid cache");
+      case CacheKind::HasOwn: {
+        IonHasOwnIC* ic = ic_->asHasOwnIC();
+        Register output = ic->output();
+
+        available.add(output);
+
+        liveRegs_.emplace(ic->liveRegs());
+        outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Boolean, AnyRegister(output)));
+
+        MOZ_ASSERT(numInputs == 2);
+        allocator.initInputLocation(0, ic->id());
+        allocator.initInputLocation(1, ic->value());
+        break;
+      }
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
 
     allocator.initAvailableRegs(available);
     allocator.initAvailableRegsAfterSpill();
     return true;
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -43,16 +43,18 @@ IonIC::scratchRegisterForEntryJump()
       }
       case CacheKind::SetProp:
       case CacheKind::SetElem:
         return asSetPropertyIC()->temp();
       case CacheKind::GetName:
         return asGetNameIC()->temp();
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
+      case CacheKind::HasOwn:
+        return asHasOwnIC()->output();
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 IonIC::discardStubs(Zone* zone)
 {
@@ -316,16 +318,46 @@ IonGetNameIC::update(JSContext* cx, Hand
     }
 
     // No need to call TypeScript::Monitor, IonBuilder always inserts a type
     // barrier after GetName ICs.
 
     return true;
 }
 
+/* static */ bool
+IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
+                    HandleValue val, HandleValue idVal, int32_t* res)
+{
+    IonScript* ionScript = outerScript->ionScript();
+
+    if (ic->state().maybeTransition())
+        ic->discardStubs(cx->zone());
+
+    jsbytecode* pc = ic->pc();
+
+    if (ic->state().canAttachStub()) {
+        bool attached = false;
+        RootedScript script(cx, ic->script());
+        HasOwnIRGenerator gen(cx, script, pc, ic->state().mode(), idVal, val);
+        if (gen.tryAttachStub())
+            ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
+
+        if (!attached)
+            ic->state().trackNotAttached();
+    }
+
+    bool found;
+    if (!HasOwnProperty(cx, val, idVal, &found))
+        return false;
+
+    *res = found;
+    return true;
+}
+
 uint8_t*
 IonICStub::stubDataStart()
 {
     return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
 }
 
 void
 IonIC::attachStub(IonICStub* newStub, JitCode* code)
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -54,16 +54,17 @@ class IonICStub
         next_ = nullptr;
         stubInfo_ = nullptr;
     }
 };
 
 class IonGetPropertyIC;
 class IonSetPropertyIC;
 class IonGetNameIC;
+class IonHasOwnIC;
 
 class IonIC
 {
     // This either points at the OOL path for the fallback path, or the code for
     // the first stub.
     uint8_t* codeRaw_;
 
     // The first optimized stub, or nullptr.
@@ -139,16 +140,20 @@ class IonIC
     IonSetPropertyIC* asSetPropertyIC() {
         MOZ_ASSERT(kind_ == CacheKind::SetProp || kind_ == CacheKind::SetElem);
         return (IonSetPropertyIC*)this;
     }
     IonGetNameIC* asGetNameIC() {
         MOZ_ASSERT(kind_ == CacheKind::GetName);
         return (IonGetNameIC*)this;
     }
+    IonHasOwnIC* asHasOwnIC() {
+        MOZ_ASSERT(kind_ == CacheKind::HasOwn);
+        return (IonHasOwnIC*)this;
+    }
 
     void updateBaseAddress(JitCode* code, MacroAssembler& masm);
 
     // Returns the Register to use as scratch when entering IC stubs. This
     // should either be an output register or a temp.
     Register scratchRegisterForEntryJump();
 
     void trace(JSTracer* trc);
@@ -270,12 +275,39 @@ class IonGetNameIC : public IonIC
     ValueOperand output() const { return output_; }
     Register temp() const { return temp_; }
     LiveRegisterSet liveRegs() const { return liveRegs_; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetNameIC* ic,
                                     HandleObject envChain, MutableHandleValue res);
 };
 
+class IonHasOwnIC : public IonIC
+{
+    LiveRegisterSet liveRegs_;
+
+    TypedOrValueRegister value_;
+    TypedOrValueRegister id_;
+    Register output_;
+
+  public:
+    IonHasOwnIC(LiveRegisterSet liveRegs, TypedOrValueRegister value,
+                TypedOrValueRegister id, Register output)
+      : IonIC(CacheKind::HasOwn),
+        liveRegs_(liveRegs),
+        value_(value),
+        id_(id),
+        output_(output)
+    { }
+
+    TypedOrValueRegister value() const { return value_; }
+    TypedOrValueRegister id() const { return id_; }
+    Register output() const { return output_; }
+    LiveRegisterSet liveRegs() const { return liveRegs_; }
+
+    static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
+                                    HandleValue val, HandleValue idVal, int32_t* res);
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonIC_h */
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4182,16 +4182,37 @@ LIRGenerator::visitIn(MIn* ins)
     MOZ_ASSERT(rhs->type() == MIRType::Object);
 
     LIn* lir = new(alloc()) LIn(useBoxAtStart(lhs), useRegisterAtStart(rhs));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitHasOwnCache(MHasOwnCache* ins)
+{
+    MDefinition* value = ins->value();
+    MOZ_ASSERT(value->type() == MIRType::Object || value->type() == MIRType::Value);
+
+    MDefinition* id = ins->idval();
+    MOZ_ASSERT(id->type() == MIRType::String ||
+               id->type() == MIRType::Symbol ||
+               id->type() == MIRType::Int32 ||
+               id->type() == MIRType::Value);
+
+    gen->setPerformsCall();
+
+    LHasOwnCache* lir =
+        new(alloc()) LHasOwnCache(useBoxOrTyped(value),
+                                  useBoxOrTyped(id));
+    define(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitInstanceOf(MInstanceOf* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
 
     MOZ_ASSERT(lhs->type() == MIRType::Value || lhs->type() == MIRType::Object);
 
     if (lhs->type() == MIRType::Object) {
         LInstanceOfO* lir = new(alloc()) LInstanceOfO(useRegister(lhs));
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -283,16 +283,17 @@ class LIRGenerator : public LIRGenerator
     void visitArgumentsLength(MArgumentsLength* ins);
     void visitGetFrameArgument(MGetFrameArgument* ins);
     void visitSetFrameArgument(MSetFrameArgument* ins);
     void visitRunOncePrologue(MRunOncePrologue* ins);
     void visitRest(MRest* ins);
     void visitThrow(MThrow* ins);
     void visitIn(MIn* ins);
     void visitInArray(MInArray* ins);
+    void visitHasOwnCache(MHasOwnCache* ins);
     void visitInstanceOf(MInstanceOf* ins);
     void visitCallInstanceOf(MCallInstanceOf* ins);
     void visitIsCallable(MIsCallable* ins);
     void visitIsConstructor(MIsConstructor* ins);
     void visitIsObject(MIsObject* ins);
     void visitHasClass(MHasClass* ins);
     void visitWasmAddOffset(MWasmAddOffset* ins);
     void visitWasmLoadTls(MWasmLoadTls* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -12541,16 +12541,32 @@ class MInArray
         if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
             return false;
         if (unboxedType() != other->unboxedType())
             return false;
         return congruentIfOperandsEqual(other);
     }
 };
 
+class MHasOwnCache
+  : public MBinaryInstruction,
+    public MixPolicy<BoxExceptPolicy<0, MIRType::Object>, CacheIdPolicy<1>>::Data
+{
+    MHasOwnCache(MDefinition* obj, MDefinition* id)
+      : MBinaryInstruction(obj, id)
+    {
+        setResultType(MIRType::Boolean);
+    }
+
+  public:
+    INSTRUCTION_HEADER(HasOwnCache)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, value), (1, idval))
+};
+
 // Implementation for instanceof operator with specific rhs.
 class MInstanceOf
   : public MUnaryInstruction,
     public InstanceOfPolicy::Data
 {
     CompilerObject protoObj_;
 
     MInstanceOf(MDefinition* obj, JSObject* proto)
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -262,16 +262,17 @@ namespace jit {
     _(SetFrameArgument)                                                     \
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Ceil)                                                                 \
     _(Round)                                                                \
     _(NearbyInt)                                                            \
     _(In)                                                                   \
+    _(HasOwnCache)                                                          \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsConstructor)                                                        \
     _(IsCallable)                                                           \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -7861,16 +7861,34 @@ class LIn : public LCallInstructionHelpe
     const LAllocation* rhs() {
         return getOperand(RHS);
     }
 
     static const size_t LHS = 0;
     static const size_t RHS = BOX_PIECES;
 };
 
+class LHasOwnCache : public LInstructionHelper<1, 2 * BOX_PIECES, 0>
+{
+  public:
+    LIR_HEADER(HasOwnCache)
+
+    static const size_t Value = 0;
+    static const size_t Id = BOX_PIECES;
+
+    LHasOwnCache(const LBoxAllocation& value, const LBoxAllocation& id) {
+        setBoxOperand(Value, value);
+        setBoxOperand(Id, id);
+    }
+
+    const MHasOwnCache* mir() const {
+        return mir_->toHasOwnCache();
+    }
+};
+
 class LInstanceOfO : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(InstanceOfO)
     explicit LInstanceOfO(const LAllocation& lhs) {
         setOperand(0, lhs);
     }
 
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -372,16 +372,17 @@
     _(Ceil)                         \
     _(CeilF)                        \
     _(Round)                        \
     _(RoundF)                       \
     _(NearbyInt)                    \
     _(NearbyIntF)                   \
     _(In)                           \
     _(InArray)                      \
+    _(HasOwnCache)                  \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(Rotate)                       \
     _(RotateI64)                    \
     _(GetDOMProperty)               \
     _(GetDOMMemberV)                \