Bug 768270 - Inline constructors. r=dvander
authorSean Stangl <sstangl@mozilla.com>
Thu, 28 Jun 2012 18:58:51 -0700
changeset 106486 f79d7ca811e626f9f302ef92436c28d9cb91146d
parent 106485 0cb6898fc0266e964938ee4895722e9c8dc52cc2
child 106487 97e97b5b6db1b540aaca6f837083c771b5348c70
push id23447
push userdanderson@mozilla.com
push dateTue, 11 Sep 2012 17:34:27 +0000
treeherdermozilla-central@fdfaef738a00 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs768270
milestone16.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 768270 - Inline constructors. r=dvander
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/jsinferinlines.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1241,16 +1241,41 @@ CodeGenerator::visitOutOfLineCreateThis(
 {
     if (!visitCreateThisVMCall(ool->lir()))
         return false;
     masm.jump(ool->rejoin());
     return true;
 }
 
 bool
+CodeGenerator::visitReturnFromCtor(LReturnFromCtor *lir)
+{
+    ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
+    Register obj = ToRegister(lir->getObject());
+    Register output = ToRegister(lir->output());
+
+    Label valueIsObject, end;
+
+    masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
+
+    // Value is not an object. Return that other object.
+    masm.movePtr(obj, output);
+    masm.jump(&end);
+
+    // Value is an object. Return unbox(Value).
+    masm.bind(&valueIsObject);
+    Register payload = masm.extractObject(value, output);
+    if (payload != output)
+        masm.movePtr(payload, output);
+
+    masm.bind(&end);
+    return true;
+}
+
+bool
 CodeGenerator::visitArrayLength(LArrayLength *lir)
 {
     Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
     masm.load32(length, ToRegister(lir->output()));
     return true;
 }
 
 bool
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -115,16 +115,17 @@ class CodeGenerator : public CodeGenerat
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
     bool visitNewCallObject(LNewCallObject *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitCreateThisVMCall(LCreateThis *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitOutOfLineCreateThis(OutOfLineCreateThis *ool);
+    bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
     bool visitTypedArrayElements(LTypedArrayElements *lir);
     bool visitStringLength(LStringLength *lir);
     bool visitInitializedLength(LInitializedLength *lir);
     bool visitSetInitializedLength(LSetInitializedLength *lir);
     bool visitNotV(LNotV *ins);
     bool visitBoundsCheck(LBoundsCheck *lir);
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -357,49 +357,54 @@ IonBuilder::processIterators()
 
     return true;
 }
 
 bool
 IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                         MDefinition *thisDefn, MDefinitionVector &argv)
 {
+    IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)",
+            script->filename, script->lineno, (void *)script);
+
+    callerBuilder_ = callerBuilder;
+    callerResumePoint_ = callerResumePoint;
+
+    // Generate single entrance block.
     current = newBlock(pc);
     if (!current)
         return false;
 
-    IonSpew(IonSpew_MIR, "Inlining script %s:%d (%p)",
-            script->filename, script->lineno, (void *) script);
-
-    callerBuilder_ = callerBuilder;
-    callerResumePoint_ = callerResumePoint;
+    current->setCallerResumePoint(callerResumePoint);
+
+    // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerResumePoint->block();
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
+
     JS_ASSERT(predecessor->numSuccessors() == 1);
     JS_ASSERT(current->numPredecessors() == 1);
-    current->setCallerResumePoint(callerResumePoint);
-
-    // Fill in any missing arguments with undefined.
+
+    // Explicitly pass Undefined for missing arguments.
     const size_t numActualArgs = argv.length() - 1;
     const size_t nargs = info().nargs();
-    IonSpew(IonSpew_MIR, "Inlining with %s of arguments.",
-            (numActualArgs == nargs) ? "right number": ((numActualArgs < nargs) ? "underflow" : "overflow"));
+
     if (numActualArgs < nargs) {
-        for (size_t i = 0, missing = nargs - numActualArgs; i < missing; ++i) {
+        const size_t missing = nargs - numActualArgs;
+
+        for (size_t i = 0; i < missing; i++) {
             MConstant *undef = MConstant::New(UndefinedValue());
             current->add(undef);
             if (!argv.append(undef))
                 return false;
         }
     }
 
-    // The oracle ensures that the inlined script does not use the scope chain, so we
-    // just initialize its slot to |undefined|.
+    // The Oracle ensures that the inlined script does not use the scope chain.
     JS_ASSERT(!script->analysis()->usesScopeChain());
     MInstruction *scope = MConstant::New(UndefinedValue());
     current->add(scope);
     current->initSlot(info().scopeChainSlot(), scope);
 
     current->initSlot(info().thisSlot(), thisDefn);
 
     IonSpew(IonSpew_Inlining, "Initializing %u arg slots", nargs);
@@ -420,17 +425,17 @@ IonBuilder::buildInline(IonBuilder *call
         MConstant *undef = MConstant::New(UndefinedValue());
         current->add(undef);
         current->initSlot(info().localSlot(i), undef);
     }
 
     IonSpew(IonSpew_Inlining, "Inline entry block MResumePoint %p, %u operands",
             (void *) current->entryResumePoint(), current->entryResumePoint()->numOperands());
 
-    // Note: +2 for the scope chain and |this|.
+    // +2 for the scope chain and |this|.
     JS_ASSERT(current->entryResumePoint()->numOperands() == nargs + info().nlocals() + 2);
 
     return traverseBytecode();
 }
 
 // Apply Type Inference information to parameters early on, unboxing them if
 // they have a definitive type. The actual guards will be emitted by the code
 // generator, explicitly, as part of the function prologue.
@@ -2719,80 +2724,100 @@ IonBuilder::jsop_notearg()
         return abort("NYI: escaping of the argument object.");
 
     current->add(arg);
     current->push(arg);
     return true;
 }
 
 bool
-IonBuilder::jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder)
+IonBuilder::jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee,
+                             uint32 argc, bool constructing)
 {
 #ifdef DEBUG
     uint32 origStackDepth = current->stackDepth();
 #endif
 
     // |top| jumps into the callee subgraph -- save it for later use.
     MBasicBlock *top = current;
 
     // Add the function constant before the resume point which map call
     // arguments.
     MConstant *constFun = MConstant::New(ObjectValue(*callee));
     current->add(constFun);
 
     // This resume point collects outer variables only.  It is used to recover
     // the stack state before the current bytecode.
-    MResumePoint *inlineResumePoint = MResumePoint::New(top, pc, callerResumePoint_,
-                                                        MResumePoint::Outer);
+    MResumePoint *inlineResumePoint =
+        MResumePoint::New(top, pc, callerResumePoint_, MResumePoint::Outer);
     if (!inlineResumePoint)
         return false;
 
     // We do not inline JSOP_FUNCALL for now.
     JS_ASSERT(argc == GET_ARGC(inlineResumePoint->pc()));
 
     // Gather up the arguments and |this| to the inline function.
     // Note that we leave the callee on the simulated stack for the
     // duration of the call.
     MDefinitionVector argv;
-
-    // Arguments are popped right-to-left so we have to fill |args| backwards.
     if (!discardCallArgs(argc, argv, top))
         return false;
 
     // Replace the potential object load by the corresponding constant version
     // which is inlined here. We do this to ensure that on a bailout, we can get
     // back to the caller by iterating over the stack.
     inlineResumePoint->replaceOperand(inlineResumePoint->numOperands() - (argc + 2), constFun);
     current->pop();
     current->push(constFun);
 
-    MDefinition *thisDefn = argv[0];
+    // Create |this| on the caller-side for inlined constructors.
+    MDefinition *thisDefn = NULL;
+    if (constructing) {
+        thisDefn = createThis(callee, constFun);
+        if (!thisDefn)
+            return false;
+    } else {
+        thisDefn = argv[0];
+    }
 
     // Build the graph.
     if (!inlineBuilder.buildInline(this, inlineResumePoint, thisDefn, argv))
         return false;
 
     MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
 
     // Create a |bottom| block for all the callee subgraph exits to jump to.
     JS_ASSERT(types::IsInlinableCall(pc));
     jsbytecode *postCall = GetNextPc(pc);
     MBasicBlock *bottom = newBlock(NULL, postCall);
     bottom->setCallerResumePoint(callerResumePoint_);
 
-    // Link graph exits to |bottom| via MGotos, replacing MReturns.
+    // Replace all MReturns with MGotos, and remember the MDefinition that
+    // would have been returned.
     Vector<MDefinition *, 8, IonAllocPolicy> retvalDefns;
     for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) {
         MBasicBlock *exitBlock = *it;
-        JS_ASSERT(exitBlock->lastIns()->op() == MDefinition::Op_Return);
-
-        if (!retvalDefns.append(exitBlock->lastIns()->toReturn()->getOperand(0)))
+
+        MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
+        exitBlock->discardLastIns();
+
+        // Inlined constructors return |this| unless overridden by another Object.
+        if (constructing) {
+            if (rval->type() == MIRType_Value) {
+                MReturnFromCtor *filter = MReturnFromCtor::New(rval, thisDefn);
+                rval->block()->add(filter);
+                rval = filter;
+            } else if (rval->type() != MIRType_Object) {
+                rval = thisDefn;
+            }
+        }
+
+        if (!retvalDefns.append(rval))
             return false;
 
-        exitBlock->discardLastIns();
         MGoto *replacement = MGoto::New(bottom);
         exitBlock->end(replacement);
         if (!bottom->addPredecessorWithoutPhis(exitBlock))
             return false;
     }
     JS_ASSERT(!retvalDefns.empty());
 
     if (!bottom->inheritNonPredecessor(top))
@@ -2854,17 +2879,17 @@ static bool IsSmallFunction(JSFunction *
     JSScript *script = target->script();
     if(script->length > js_IonOptions.smallFunctionMaxBytecodeLength)
         return false;
 
     return true;
 }
 
 bool
-IonBuilder::makeInliningDecision(JSFunction *target)
+IonBuilder::makeInliningDecision(HandleFunction target)
 {
     static const size_t INLINING_LIMIT = 3;
 
     if (inliningDepth >= INLINING_LIMIT)
         return false;
 
     // For "small" functions, we should be more aggressive about inlining.
     // This is based on the following intuition:
@@ -2893,17 +2918,17 @@ IonBuilder::makeInliningDecision(JSFunct
         IonSpew(IonSpew_Inlining, "Decided not to inline");
         return false;
     }
 
     return true;
 }
 
 bool
-IonBuilder::inlineScriptedCall(JSFunction *target, uint32 argc)
+IonBuilder::inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing)
 {
     IonSpew(IonSpew_Inlining, "Recursively building");
 
     // Compilation information is allocated for the duration of the current tempLifoAlloc
     // lifetime.
     CompileInfo *info = cx->tempLifoAlloc().new_<CompileInfo>(target->script(),
                                                               target, (jsbytecode *)NULL);
     if (!info)
@@ -2915,17 +2940,17 @@ IonBuilder::inlineScriptedCall(JSFunctio
     TypeInferenceOracle oracle;
     if (!oracle.init(cx, target->script()))
         return false;
 
     RootedObject scopeChain(NULL);
 
     IonBuilder inlineBuilder(cx, scopeChain, temp(), graph(), &oracle,
                              *info, inliningDepth + 1, loopDepth_);
-    return jsop_call_inline(target, argc, inlineBuilder);
+    return jsop_call_inline(inlineBuilder, target, argc, constructing);
 }
 
 void
 IonBuilder::copyFormalIntoCallObj(MDefinition *callObj, MDefinition *slots, unsigned formal)
 {
     // Note that in the case of using dynamic slots, RESERVED_SLOTS == numFixedSlots.
     MDefinition *param = current->getSlot(info().argSlot(formal));
     if (slots->type() == MIRType_Slots)
@@ -3153,18 +3178,18 @@ IonBuilder::jsop_call_fun_barrier(Handle
                 return true;
               case InliningStatus_Error:
                 return false;
               case InliningStatus_NotInlined:
                 break;
             }
         }
 
-        if (!constructing && makeInliningDecision(target))
-            return inlineScriptedCall(target, argc);
+        if (makeInliningDecision(target))
+            return inlineScriptedCall(target, argc, constructing);
     }
 
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
 
 bool
 IonBuilder::jsop_call(uint32 argc, bool constructing)
 {
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -192,18 +192,18 @@ class IonBuilder : public MIRGenerator
 
     static int CmpSuccessors(const void *a, const void *b);
 
   public:
     IonBuilder(JSContext *cx, HandleObject scopeChain, TempAllocator &temp, MIRGraph &graph,
                TypeOracle *oracle, CompileInfo &info, size_t inliningDepth = 0, uint32 loopDepth = 0);
 
     bool build();
-    bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint, MDefinition *thisDefn,
-                     MDefinitionVector &args);
+    bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
+                     MDefinition *thisDefn, MDefinitionVector &args);
 
   private:
     bool traverseBytecode();
     ControlStatus snoopControlFlow(JSOp op);
     void markPhiBytecodeUses(jsbytecode *pc);
     bool processIterators();
     bool inspectOpcode(JSOp op);
     uint32 readIndex(jsbytecode *pc);
@@ -400,19 +400,20 @@ class IonBuilder : public MIRGenerator
 
     // String natives.
     InliningStatus inlineStrCharCodeAt(uint32 argc, bool constructing);
     InliningStatus inlineStrFromCharCode(uint32 argc, bool constructing);
     InliningStatus inlineStrCharAt(uint32 argc, bool constructing);
 
     InliningStatus inlineNativeCall(JSNative native, uint32 argc, bool constructing);
 
-    bool jsop_call_inline(JSFunction *callee, uint32 argc, IonBuilder &inlineBuilder);
-    bool inlineScriptedCall(JSFunction *target, uint32 argc);
-    bool makeInliningDecision(JSFunction *target);
+    bool jsop_call_inline(IonBuilder &inlineBuilder, HandleFunction callee,
+                          uint32 argc, bool constructing);
+    bool inlineScriptedCall(HandleFunction target, uint32 argc, bool constructing);
+    bool makeInliningDecision(HandleFunction target);
 
     bool jsop_call_fun_barrier(HandleFunction target, uint32 argc, 
                                bool constructing,
 							   types::TypeSet *types,
                                types::TypeSet *barrier);
     bool makeCallBarrier(HandleFunction target, uint32 argc, bool constructing,
                          types::TypeSet *types, types::TypeSet *barrier);
 
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -426,16 +426,40 @@ class LCreateThis : public LInstructionH
         return getDef(0);
     }
 
     MCreateThis *mir() const {
         return mir_->toCreateThis();
     }
 };
 
+// If the Value is an Object, return unbox(Value).
+// Otherwise, return the other Object.
+class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0>
+{
+  public:
+    LIR_HEADER(ReturnFromCtor);
+
+    LReturnFromCtor(const LAllocation &object)
+    {
+        // Value set by useBox() during lowering.
+        setOperand(LReturnFromCtor::ObjectIndex, object);
+    }
+
+    const LAllocation *getObject() {
+        return getOperand(LReturnFromCtor::ObjectIndex);
+    }
+    const LDefinition *output() {
+        return getDef(0);
+    }
+
+    static const size_t ValueIndex = 0;
+    static const size_t ObjectIndex = BOX_PIECES;
+};
+
 // Writes an argument for a function call to the frame's argument vector.
 class LStackArg : public LInstructionHelper<0, BOX_PIECES, 0>
 {
     uint32 argslot_; // Index into frame-scope argument vector.
 
   public:
     LIR_HEADER(StackArg);
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -63,16 +63,17 @@
     _(CheckOverRecursed)            \
     _(RecompileCheck)               \
     _(DefVar)                       \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(CallConstructor)              \
     _(StackArg)                     \
     _(CreateThis)                   \
+    _(ReturnFromCtor)               \
     _(BitNotI)                      \
     _(BitNotV)                      \
     _(BitOpI)                       \
     _(BitOpV)                       \
     _(ShiftOp)                      \
     _(Return)                       \
     _(Throw)                        \
     _(Phi)                          \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -227,16 +227,26 @@ LIRGenerator::visitCreateThis(MCreateThi
 {
     LCreateThis *lir = new LCreateThis(useRegister(ins->getCallee()),
                                        useRegister(ins->getPrototype()));
     
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitReturnFromCtor(MReturnFromCtor *ins)
+{
+    LReturnFromCtor *lir = new LReturnFromCtor(useRegister(ins->getObject()));
+    if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue()))
+        return false;
+
+    return define(lir, ins);
+}
+
+bool
 LIRGenerator::visitCall(MCall *call)
 {
     JS_ASSERT(CallTempReg0 != CallTempReg1);
     JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg);
     JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg);
     JS_ASSERT(call->getFunction()->type() == MIRType_Object);
 
     // Height of the current argument vector.
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -116,16 +116,17 @@ class LIRGenerator : public LIRGenerator
     bool visitNewObject(MNewObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
     bool visitDefVar(MDefVar *ins);
     bool visitPrepareCall(MPrepareCall *ins);
     bool visitPassArg(MPassArg *arg);
     bool visitCreateThis(MCreateThis *ins);
+    bool visitReturnFromCtor(MReturnFromCtor *ins);
     bool visitCall(MCall *call);
     bool visitTest(MTest *test);
     bool visitCompare(MCompare *comp);
     bool visitTypeOf(MTypeOf *ins);
     bool visitToId(MToId *ins);
     bool visitBitNot(MBitNot *ins);
     bool visitBitAnd(MBitAnd *ins);
     bool visitBitOr(MBitOr *ins);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1516,16 +1516,52 @@ class MCreateThis
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
+// Given a MIRType_Value A and a MIRType_Object B:
+// If the Value may be safely unboxed to an Object, return Object(A).
+// Otherwise, return B.
+// Used to implement return behavior for inlined constructors.
+class MReturnFromCtor
+  : public MAryInstruction<2>,
+    public MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >
+{
+    MReturnFromCtor(MDefinition *value, MDefinition *object) {
+        initOperand(0, value);
+        initOperand(1, object);
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(ReturnFromCtor);
+    static MReturnFromCtor *New(MDefinition *value, MDefinition *object)
+    {
+        return new MReturnFromCtor(value, object);
+    }
+
+    MDefinition *getValue() const {
+        return getOperand(0);
+    }
+    MDefinition *getObject() const {
+        return getOperand(1);
+    }
+
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+    TypePolicy *typePolicy() {
+        return this;
+    }
+};
+
 // Passes an MDefinition to an MCall. Must occur between an MPrepareCall and
 // MCall. Boxes the input and stores it to the correct location on stack.
 //
 // Arguments are *not* simply pushed onto a call stack: they are evaluated
 // left-to-right, but stored in the arg vector in C-style, right-to-left.
 class MPassArg
   : public MUnaryInstruction,
     public BoxInputsPolicy
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -52,16 +52,17 @@ namespace ion {
     _(Callee)                                                               \
     _(TableSwitch)                                                          \
     _(Goto)                                                                 \
     _(Test)                                                                 \
     _(Compare)                                                              \
     _(Phi)                                                                  \
     _(OsrValue)                                                             \
     _(OsrScopeChain)                                                        \
+    _(ReturnFromCtor)                                                       \
     _(CheckOverRecursed)                                                    \
     _(RecompileCheck)                                                       \
     _(DefVar)                                                               \
     _(CreateThis)                                                           \
     _(PrepareCall)                                                          \
     _(PassArg)                                                              \
     _(Call)                                                                 \
     _(BitNot)                                                               \
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -154,26 +154,30 @@ TypeIdString(jsid id)
 {
 #ifdef DEBUG
     return TypeIdStringImpl(id);
 #else
     return "(missing)";
 #endif
 }
 
-/* Assert code to know which PCs are reasonable to be considering inlining on */
+/* Assert code to know which PCs are reasonable to be considering inlining on. */
 inline bool
 IsInlinableCall(jsbytecode *pc)
 {
     JSOp op = JSOp(*pc);
 
     // CALL, FUNCALL, FUNAPPLY (Standard callsites)
+    // NEW (IonMonkey-only callsite)
     // GETPROP, CALLPROP, and LENGTH. (Inlined Getters)
     // SETPROP, SETNAME, SETGNAME (Inlined Setters)
     return op == JSOP_CALL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY ||
+#ifdef JS_ION
+           op == JSOP_NEW ||
+#endif
            op == JSOP_GETPROP || op == JSOP_CALLPROP || op == JSOP_LENGTH ||
            op == JSOP_SETPROP || op == JSOP_SETGNAME || op == JSOP_SETNAME;
 
 }
 
 /*
  * Structure for type inference entry point functions. All functions which can
  * change type information must use this, and functions which depend on