diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index a404d58206f..0190d0ef8f7 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -151,6 +151,12 @@ struct Bytecode */ inline types::TypeObject* getInitObject(JSContext *cx, bool isArray); + /* + * Get the type tag which values in this set must have, or JSVAL_TYPE_UNKNOWN + * if the type tag is not known. + */ + inline JSValueType getKnownTypeTag(); + void print(JSContext *cx, FILE *out); #endif /* JS_TYPE_INFERENCE */ diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 44bc0c11b31..7a9fdd06ede 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -1548,22 +1548,6 @@ Script::parentCode() return parent ? &parent->analysis->getCode(parentpc) : NULL; } -inline TypeSet* -Script::getStackTypes(unsigned index, TypeStack *stack) -{ - JS_ASSERT(index >= script->nfixed); - - stack = stack->group(); - while (stack && (stack->stackDepth != index - script->nfixed)) { - stack = stack->innerStack; - stack = stack ? stack->group() : NULL; - } - - /* This should not be used for accessing a let variable's stack slot. */ - JS_ASSERT(stack && !JSID_IS_VOID(stack->letVariable)); - return &stack->types; -} - inline Script* Script::evalParent() { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 9656286caf2..e07a6aa1c7b 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -229,6 +229,9 @@ struct TypeSet void addFreezeProp(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, jsid id); void addFreezeElem(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *object); + /* Get any type tag which all values in this set must have. */ + inline JSValueType getKnownTypeTag(); + /* * Make an intermediate type set with the specified debugging name, * not embedded in another structure. diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index a630092c887..6620f811458 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -37,7 +37,7 @@ * * ***** END LICENSE BLOCK ***** */ -// inline members for javascript type inference. +/* Inline members for javascript type inference. */ #include "jsanalyze.h" #include "jscompartment.h" @@ -767,6 +767,22 @@ Script::getArgumentId(unsigned index) return ATOM_TO_JSID(JS_LOCAL_NAME_TO_ATOM(localNames[index])); } +inline types::TypeSet* +Script::getStackTypes(unsigned index, types::TypeStack *stack) +{ + JS_ASSERT(index >= script->nfixed); + + stack = stack->group(); + while (stack && (stack->stackDepth != index - script->nfixed)) { + stack = stack->innerStack; + stack = stack ? stack->group() : NULL; + } + + /* This should not be used for accessing a let variable's stack slot. */ + JS_ASSERT(stack && !JSID_IS_VOID(stack->letVariable)); + return &stack->types; +} + } /* namespace analyze */ ///////////////////////////////////////////////////////////////////// @@ -1117,6 +1133,27 @@ TypeSet::addType(JSContext *cx, jstype type) cx->compartment->types.resolvePending(cx); } +inline JSValueType +TypeSet::getKnownTypeTag() +{ + switch (typeFlags) { + case TYPE_FLAG_UNDEFINED: + return JSVAL_TYPE_UNDEFINED; + case TYPE_FLAG_NULL: + return JSVAL_TYPE_NULL; + case TYPE_FLAG_BOOLEAN: + return JSVAL_TYPE_BOOLEAN; + case TYPE_FLAG_INT32: + return JSVAL_TYPE_INT32; + case TYPE_FLAG_STRING: + return JSVAL_TYPE_STRING; + case TYPE_FLAG_OBJECT: + return JSVAL_TYPE_OBJECT; + default: + return JSVAL_TYPE_UNKNOWN; + } +} + inline TypeSet * TypeSet::make(JSContext *cx, JSArenaPool &pool, const char *name) { diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 49806771248..34e2c9f8c0a 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -266,8 +266,8 @@ TypeToChar(JSValueType type) case JSVAL_TYPE_STRORNULL: return 's'; case JSVAL_TYPE_OBJORNULL: return 'o'; case JSVAL_TYPE_UNINITIALIZED: return '*'; + default: return '?'; } - return '?'; } static char diff --git a/js/src/jsval.h b/js/src/jsval.h index d6d4c65564d..93450c28956 100644 --- a/js/src/jsval.h +++ b/js/src/jsval.h @@ -100,7 +100,9 @@ JS_ENUM_HEADER(JSValueType, uint8) JSVAL_TYPE_NULL = 0x06, JSVAL_TYPE_OBJECT = 0x07, - /* The below types never appear in a jsval; they are only used in tracing. */ + /* The below types never appear in a jsval; they are only used in tracing and type inference. */ + + JSVAL_TYPE_UNKNOWN = 0x20, JSVAL_TYPE_NONFUNOBJ = 0x57, JSVAL_TYPE_FUNOBJ = 0x67, diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 4d5926d01a9..e7d63f8d3a7 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -345,6 +345,20 @@ mjit::Compiler::generatePrologue() if (debugMode) stubCall(stubs::EnterScript); + /* + * Set initial types of locals with known type. These will stay synced + * through the rest of the script. + */ + for (uint32 i = 0; i < script->nfixed; i++) { + JSValueType type = knownLocalType(i); + if (type != JSVAL_TYPE_UNKNOWN) { + JS_ASSERT(!analysis->localHasUseBeforeDef(i)); + Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value)); + masm.storeTypeTag(ImmType(type), local); + frame.learnType(frame.getLocal(i), type, false); + } + } + return Compile_Okay; } @@ -808,6 +822,9 @@ mjit::Compiler::generateMethod() if (opinfo->jumpTarget || trap) { frame.syncAndForgetEverything(opinfo->stackDepth); opinfo->safePoint = true; + + if (!trap) + restoreAnalysisTypes(opinfo->stackDepth); } jumpMap[uint32(PC - script->code)] = masm.label(); @@ -1428,7 +1445,8 @@ mjit::Compiler::generateMethod() { jsbytecode *next = &PC[JSOP_SETLOCAL_LENGTH]; bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next); - frame.storeLocal(GET_SLOTNO(PC), pop); + JSValueType type = knownLocalType(GET_SLOTNO(PC)); + frame.storeLocal(GET_SLOTNO(PC), pop, type == JSVAL_TYPE_UNKNOWN); if (pop) { frame.pop(); PC += JSOP_SETLOCAL_LENGTH + JSOP_POP_LENGTH; @@ -2312,9 +2330,16 @@ mjit::Compiler::emitUncachedCall(uint32 argc, bool callingNew) masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); frame.popn(argc + 2); - frame.takeReg(JSReturnReg_Type); - frame.takeReg(JSReturnReg_Data); - frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + + JSValueType type = knownPushedType(0); + if (type != JSVAL_TYPE_UNKNOWN) { + frame.takeReg(JSReturnReg_Data); + frame.pushTypedPayload(type, JSReturnReg_Data); + } else { + frame.takeReg(JSReturnReg_Type); + frame.takeReg(JSReturnReg_Data); + frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + } stubcc.linkExitDirect(notCompiled, stubcc.masm.label()); stubcc.rejoin(Changes(0)); @@ -2468,9 +2493,16 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew) masm.loadPtr(Address(JSFrameReg, JSStackFrame::offsetOfPrev()), JSFrameReg); frame.popn(argc + 2); - frame.takeReg(JSReturnReg_Type); - frame.takeReg(JSReturnReg_Data); - frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + + JSValueType type = knownPushedType(0); + if (type != JSVAL_TYPE_UNKNOWN) { + frame.takeReg(JSReturnReg_Data); + frame.pushTypedPayload(type, JSReturnReg_Data); + } else { + frame.takeReg(JSReturnReg_Type); + frame.takeReg(JSReturnReg_Data); + frame.pushRegs(JSReturnReg_Type, JSReturnReg_Data); + } callIC.slowJoinPoint = stubcc.masm.label(); rejoin1.linkTo(callIC.slowJoinPoint, &stubcc.masm); @@ -3444,7 +3476,16 @@ mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache) void mjit::Compiler::jsop_getarg(uint32 slot) { - frame.push(Address(JSFrameReg, JSStackFrame::offsetOfFormalArg(fun, slot))); + Address argument(JSFrameReg, JSStackFrame::offsetOfFormalArg(fun, slot)); + + JSValueType type = knownArgumentType(slot); + if (type != JSVAL_TYPE_UNKNOWN) { + RegisterID dataReg = frame.allocReg(); + masm.loadPayload(argument, dataReg); + frame.pushTypedPayload(type, dataReg); + } else { + frame.push(argument); + } } void @@ -4573,3 +4614,56 @@ mjit::Compiler::jsop_callelem_slow() frame.pushSynced(); } +void +mjit::Compiler::restoreAnalysisTypes(uint32 stackDepth) +{ +#ifdef JS_TYPE_INFERENCE + /* Restore known types of locals. */ + for (uint32 i = 0; i < script->nfixed; i++) { + JSValueType type = knownLocalType(i); + if (type != JSVAL_TYPE_UNKNOWN) { + FrameEntry *fe = frame.getLocal(i); + frame.learnType(fe, type, false); + } + } + for (uint32 i = 0; i < stackDepth; i++) { + types::TypeStack *stack = analysis->getCode(PC).inStack; + JSValueType type = analysis->getStackTypes(script->nfixed + i, stack)->getKnownTypeTag(); + if (type != JSVAL_TYPE_UNKNOWN) { + FrameEntry *fe = frame.getLocal(script->nfixed + i); + frame.learnType(fe, type, true); + } + } +#endif +} + +JSValueType +mjit::Compiler::knownArgumentType(uint32 arg) +{ +#ifdef JS_TYPE_INFERENCE + jsid id = analysis->getArgumentId(arg); + if (!JSID_IS_VOID(id)) + return analysis->localTypes.getVariable(cx, id)->getKnownTypeTag(); +#endif + return JSVAL_TYPE_UNKNOWN; +} + +JSValueType +mjit::Compiler::knownLocalType(uint32 local) +{ +#ifdef JS_TYPE_INFERENCE + jsid id = analysis->getLocalId(local, NULL); + if (!analysis->localHasUseBeforeDef(local) && !JSID_IS_VOID(id)) + return analysis->localTypes.getVariable(cx, id)->getKnownTypeTag(); +#endif + return JSVAL_TYPE_UNKNOWN; +} + +JSValueType +mjit::Compiler::knownPushedType(uint32 pushed) +{ +#ifdef JS_TYPE_INFERENCE + return analysis->getCode(PC).pushed(pushed)->getKnownTypeTag(); +#endif + return JSVAL_TYPE_UNKNOWN; +} diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index ff5c102cab1..52007333ef0 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -319,6 +319,10 @@ class Compiler : public BaseCompiler bool jumpInScript(Jump j, jsbytecode *pc); bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs); void addCallSite(uint32 id, bool stub); + void restoreAnalysisTypes(uint32 stackDepth); + JSValueType knownArgumentType(uint32 arg); + JSValueType knownLocalType(uint32 local); + JSValueType knownPushedType(uint32 pushed); /* Emitting helpers. */ void restoreFrameRegs(Assembler &masm); diff --git a/js/src/methodjit/FastArithmetic.cpp b/js/src/methodjit/FastArithmetic.cpp index ce1b336428a..b3381852831 100644 --- a/js/src/methodjit/FastArithmetic.cpp +++ b/js/src/methodjit/FastArithmetic.cpp @@ -460,7 +460,12 @@ mjit::Compiler::jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub) /* Finish up stack operations. */ frame.popn(2); - frame.pushNumber(regs.result, true); + + JSValueType type = knownPushedType(0); + if (type != JSVAL_TYPE_UNKNOWN) + frame.pushTypedPayload(type, regs.result); + else + frame.pushNumber(regs.result, true); /* Merge back OOL double paths. */ if (doublePathDone.isSet()) @@ -707,7 +712,12 @@ mjit::Compiler::jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, Void /* Finish up stack operations. */ frame.popn(2); - frame.pushNumber(regs.result, true); + + JSValueType type = knownPushedType(0); + if (type != JSVAL_TYPE_UNKNOWN) + frame.pushTypedPayload(type, regs.result); + else + frame.pushNumber(regs.result, true); /* Merge back OOL double paths. */ if (doublePathDone.isSet()) @@ -934,7 +944,12 @@ mjit::Compiler::jsop_mod() } frame.popn(2); - frame.pushNumber(X86Registers::edx); + + JSValueType type = knownPushedType(0); + if (type != JSVAL_TYPE_UNKNOWN) + frame.pushTypedPayload(type, X86Registers::edx); + else + frame.pushNumber(X86Registers::edx, true); if (slowPath) stubcc.rejoin(Changes(1)); diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 14c256d2d79..39fb9b9fc14 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -566,7 +566,11 @@ mjit::Compiler::jsop_bitop(JSOp op) frame.pop(); frame.pop(); - if (op == JSOP_URSH) + JSValueType type = knownPushedType(0); + + if (type != JSVAL_TYPE_UNKNOWN) + frame.pushTypedPayload(type, reg); + else if (op == JSOP_URSH) frame.pushNumber(reg, true); else frame.pushTypedPayload(JSVAL_TYPE_INT32, reg); @@ -1160,8 +1164,14 @@ mjit::Compiler::jsop_arginc(JSOp op, uint32 slot, bool popped) frame.dup(); FrameEntry *fe = frame.peek(-1); - Jump notInt = frame.testInt32(Assembler::NotEqual, fe); - stubcc.linkExit(notInt, Uses(0)); + + if (fe->isTypeKnown()) { + if (fe->getKnownType() != JSVAL_TYPE_INT32) + stubcc.linkExit(masm.jump(), Uses(0)); + } else { + Jump notInt = frame.testInt32(Assembler::NotEqual, fe); + stubcc.linkExit(notInt, Uses(0)); + } RegisterID reg = frame.ownRegForData(fe); frame.pop(); diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 884eae716f7..bf35cda6d78 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -688,7 +688,7 @@ FrameState::forgetType(FrameEntry *fe) } inline void -FrameState::learnType(FrameEntry *fe, JSValueType type) +FrameState::learnType(FrameEntry *fe, JSValueType type, bool unsync) { if (fe->type.inRegister()) forgetReg(fe->type.reg()); @@ -696,6 +696,8 @@ FrameState::learnType(FrameEntry *fe, JSValueType type) fe->isNumber = false; #endif fe->setType(type); + if (unsync) + fe->type.unsync(); } inline JSC::MacroAssembler::Address diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 4ade98dd62f..b2fb2b0470d 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -1192,6 +1192,8 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) target->setCopyOf(NULL); target->setNotCopied(); target->setConstant(Jsvalify(top->getValue())); + if (!typeChange) + target->type.sync(); return; } @@ -1284,6 +1286,13 @@ FrameState::storeTop(FrameEntry *target, bool popGuaranteed, bool typeChange) regstate[reg].reassociate(target); } } else { + /* + * :FIXME: Should eventually assert this, but can't yet as registers are still + * used to hold things with known types in some places. + */ + // JS_ASSERT(backing->isTypeKnown()); + if (!backing->isTypeKnown() && backing->type.inRegister()) + forgetReg(backing->type.reg()); if (!wasSynced) masm.storeTypeTag(ImmType(backing->getKnownType()), addressOf(target)); target->type.setMemory(); diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 546c51c3987..086695e08b5 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -335,6 +335,8 @@ class FrameState */ void pushLocal(uint32 n); + inline FrameEntry *getLocal(uint32 slot); + /* * Allocates a temporary register for a FrameEntry's type. The register * can be spilled or clobbered by the frame. The compiler may only operate @@ -601,7 +603,7 @@ class FrameState /* * Mark an existing slot with a type. */ - inline void learnType(FrameEntry *fe, JSValueType type); + inline void learnType(FrameEntry *fe, JSValueType type, bool unsync = true); /* * Forget a type, syncing in the process. @@ -768,7 +770,6 @@ class FrameState inline void syncType(FrameEntry *fe); inline void syncData(FrameEntry *fe); - inline FrameEntry *getLocal(uint32 slot); inline void forgetAllRegs(FrameEntry *fe); inline void swapInTracker(FrameEntry *lhs, FrameEntry *rhs); inline uint32 localIndex(uint32 n);