[INFER] Use inferred types for locals, arg reads and some operations, bug 608750.

This commit is contained in:
Brian Hackett 2010-11-01 20:03:46 -07:00
parent d0775091ec
commit 545b668654
13 changed files with 203 additions and 36 deletions

View File

@ -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 */

View File

@ -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()
{

View File

@ -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.

View File

@ -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)
{

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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));

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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);