mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Use inferred types for locals, arg reads and some operations, bug 608750.
This commit is contained in:
parent
d0775091ec
commit
545b668654
@ -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 */
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user