[INFER] Add jitcode assertions for type correctness around property accesses, bug 685186.

This commit is contained in:
Brian Hackett 2011-09-15 16:19:38 -07:00
parent 72e409987d
commit 0d13c1627d
8 changed files with 166 additions and 17 deletions

View File

@ -1152,7 +1152,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist
* in the specified set. Updates mismatches with any failure jumps. Assumes
* no data registers are live.
*/
bool generateTypeCheck(JSContext *cx, Address address,
bool generateTypeCheck(JSContext *cx, Address address, RegisterID reg,
types::TypeSet *types, Vector<Jump> *mismatches)
{
if (types->unknown())
@ -1200,9 +1200,6 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist
if (count != 0) {
if (!mismatches->append(testObject(Assembler::NotEqual, address)))
return false;
Registers tempRegs(Registers::AvailRegs);
RegisterID reg = tempRegs.takeAnyReg().reg();
loadPayload(address, reg);
Jump notSingleton = branchTest32(Assembler::Zero,

View File

@ -2034,6 +2034,9 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_SETELEM)
{
typeCheckPopped(0);
typeCheckPopped(1);
typeCheckPopped(2);
jsbytecode *next = &PC[JSOP_SETELEM_LENGTH];
bool pop = (JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next));
if (!jsop_setelem(pop))
@ -2384,18 +2387,13 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_BINDNAME)
BEGIN_CASE(JSOP_SETPROP)
{
jsbytecode *next = &PC[JSOP_SETPROP_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
return Compile_Error;
}
END_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETMETHOD)
{
jsbytecode *next = &PC[JSOP_SETNAME_LENGTH];
typeCheckPopped(0);
if (op != JSOP_SETNAME)
typeCheckPopped(1);
jsbytecode *next = &PC[JSOP_SETPROP_LENGTH];
bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true, pop))
return Compile_Error;
@ -2793,6 +2791,22 @@ mjit::Compiler::generateMethod()
frame.extra(fe).types = analysis->pushedTypes(lastPC - script->code, i);
}
}
#ifdef DEBUG
if ((js_CodeSpec[op].format & JOF_TYPESET) &&
js_GetOpcode(cx, script, PC) != JSOP_POP) {
FrameEntry *fe = frame.getStack(opinfo->stackDepth - nuses);
Jump j = frame.typeCheckEntry(fe, frame.extra(fe).types);
stubcc.linkExit(j, Uses(0));
stubcc.leave();
jsbytecode *oldPC = PC;
PC = lastPC;
OOL_STUBCALL(stubs::TypeCheckPushed, REJOIN_FALLTHROUGH);
PC = oldPC;
stubcc.rejoin(Changes(0));
}
#endif
}
if (script->pcCounters) {
@ -7602,8 +7616,11 @@ mjit::Compiler::testPushedType(RejoinState rejoin, int which, bool ool)
JS_ASSERT(which <= 0);
Address address = (which == 0) ? frame.addressOfTop() : frame.addressOf(frame.peek(which));
Registers tempRegs(Registers::AvailRegs);
RegisterID scratch = tempRegs.takeAnyReg().reg();
Vector<Jump> mismatches(cx);
if (!masm.generateTypeCheck(cx, address, types, &mismatches)) {
if (!masm.generateTypeCheck(cx, address, scratch, types, &mismatches)) {
oomInVector = true;
return;
}
@ -7621,3 +7638,21 @@ mjit::Compiler::testPushedType(RejoinState rejoin, int which, bool ool)
j.linkTo(masm.label(), &masm);
}
#ifdef DEBUG
void
mjit::Compiler::typeCheckPopped(int which)
{
if (!cx->typeInferenceEnabled())
return;
FrameEntry *fe = frame.peek(-1 - which);
Jump j = frame.typeCheckEntry(fe, analysis->poppedTypes(PC, which));
stubcc.linkExit(j, Uses(0));
stubcc.leave();
stubcc.masm.move(Imm32(which), Registers::ArgReg1);
OOL_STUBCALL(stubs::TypeCheckPopped, REJOIN_RESUME);
stubcc.rejoin(Changes(0));
}
#endif /* DEBUG */

View File

@ -558,6 +558,9 @@ class Compiler : public BaseCompiler
CompileStatus addInlineFrame(JSScript *script, uint32 depth, uint32 parent, jsbytecode *parentpc);
CompileStatus scanInlineCalls(uint32 index, uint32 depth);
CompileStatus checkAnalysis(JSScript *script);
#ifdef DEBUG
void typeCheckPopped(int which);
#endif
struct BarrierState {
MaybeJump jump;

View File

@ -1107,6 +1107,77 @@ FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
unpinReg(address.base);
}
#ifdef DEBUG
JSC::MacroAssembler::Jump
FrameState::typeCheckEntry(const FrameEntry *fe, types::TypeSet *types) const
{
if (fe->isCopy())
fe = fe->copyOf();
Address addr1 = addressOfTop();
Address addr2 = Address(JSFrameReg, addr1.offset + sizeof(Value));
Registers tempRegs(Registers::AvailRegs);
RegisterID scratch = tempRegs.takeAnyReg().reg();
masm.storePtr(scratch, addr1);
do {
if (fe->isConstant()) {
masm.storeValue(fe->getValue(), addr2);
break;
}
if (fe->data.inFPRegister()) {
masm.storeDouble(fe->data.fpreg(), addr2);
break;
}
if (fe->isType(JSVAL_TYPE_DOUBLE)) {
JS_ASSERT(fe->data.inMemory());
masm.loadDouble(addressOf(fe), Registers::FPConversionTemp);
masm.storeDouble(Registers::FPConversionTemp, addr2);
break;
}
if (fe->data.inRegister())
masm.storePayload(fe->data.reg(), addr2);
else
JS_ASSERT(fe->data.inMemory());
if (fe->isTypeKnown())
masm.storeTypeTag(ImmType(fe->getKnownType()), addr2);
else if (fe->type.inRegister())
masm.storeTypeTag(fe->type.reg(), addr2);
else
JS_ASSERT(fe->type.inMemory());
if (fe->data.inMemory()) {
masm.loadPayload(addressOf(fe), scratch);
masm.storePayload(scratch, addr2);
}
if (fe->type.inMemory()) {
masm.loadTypeTag(addressOf(fe), scratch);
masm.storeTypeTag(scratch, addr2);
}
} while (false);
Vector<Jump> mismatches(cx);
masm.generateTypeCheck(cx, addr2, scratch, types, &mismatches);
masm.loadPtr(addr1, scratch);
Jump j = masm.jump();
for (unsigned i = 0; i < mismatches.length(); i++)
mismatches[i].linkTo(masm.label(), &masm);
masm.loadPtr(addr1, scratch);
Jump mismatch = masm.jump();
j.linkTo(masm.label(), &masm);
return mismatch;
}
#endif /* DEBUG */
void
FrameState::loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg)
{

View File

@ -618,9 +618,19 @@ class FrameState
*/
inline FrameEntry *peek(int32 depth);
#ifdef DEBUG
/*
* Check that a frame entry matches a type, returning a jump taken on
* mismatch. Does not affect register state or sync state of any entries.
*/
Jump typeCheckEntry(const FrameEntry *fe, types::TypeSet *types) const;
#endif
/*
* Fully stores a FrameEntry at an arbitrary address. popHint specifies
* how hard the register allocator should try to keep the FE in registers.
* If scratchData and scratchType are specified, the frame entry and
* register state will not be modified.
*/
void storeTo(FrameEntry *fe, Address address, bool popHint = false);

View File

@ -623,7 +623,7 @@ mjit::NativeStubEpilogue(VMFrame &f, Assembler &masm, NativeStubLinker::FinalJum
* FastBuiltins.
*/
types::TypeSet *types = f.script()->analysis()->bytecodeTypes(f.pc());
if (!masm.generateTypeCheck(f.cx, resultAddress, types, &mismatches))
if (!masm.generateTypeCheck(f.cx, resultAddress, Registers::ReturnReg, types, &mismatches))
THROWV(false);
}
}
@ -1339,17 +1339,20 @@ ic::GenerateArgumentCheckStub(VMFrame &f)
Assembler masm;
Vector<Jump> mismatches(f.cx);
Registers tempRegs(Registers::AvailRegs);
RegisterID scratch = tempRegs.takeAnyReg().reg();
if (!f.fp()->isConstructing()) {
types::TypeSet *types = types::TypeScript::ThisTypes(script);
Address address(JSFrameReg, StackFrame::offsetOfThis(fun));
if (!masm.generateTypeCheck(f.cx, address, types, &mismatches))
if (!masm.generateTypeCheck(f.cx, address, scratch, types, &mismatches))
return;
}
for (unsigned i = 0; i < fun->nargs; i++) {
types::TypeSet *types = types::TypeScript::ArgTypes(script, i);
Address address(JSFrameReg, StackFrame::offsetOfFormalArg(fun, i));
if (!masm.generateTypeCheck(f.cx, address, types, &mismatches))
if (!masm.generateTypeCheck(f.cx, address, scratch, types, &mismatches))
return;
}

View File

@ -2458,6 +2458,34 @@ stubs::AssertArgumentTypes(VMFrame &f)
TypeFailure(f.cx, "Missing type for arg %d: %s", i, TypeString(type));
}
}
void JS_FASTCALL
stubs::TypeCheckPushed(VMFrame &f)
{
TypeScript::CheckBytecode(f.cx, f.script(), f.pc(), f.regs.sp);
}
void JS_FASTCALL
stubs::TypeCheckPopped(VMFrame &f, int32 which)
{
JSScript *script = f.script();
jsbytecode *pc = f.pc();
if (!script->hasAnalysis() || !script->analysis()->ranInference())
return;
AutoEnterTypeInference enter(f.cx);
const js::Value &val = f.regs.sp[-1 - which];
TypeSet *types = script->analysis()->poppedTypes(pc, which);
Type type = GetValueType(f.cx, val);
if (!types->hasType(type)) {
/* Display fine-grained debug information first */
fprintf(stderr, "Missing type at #%u:%05u popped %u: %s\n",
script->id(), unsigned(pc - script->code), which, TypeString(type));
TypeFailure(f.cx, "Missing type popped %u", which);
}
}
#endif
/*

View File

@ -216,6 +216,8 @@ void JS_FASTCALL CheckArgumentTypes(VMFrame &f);
#ifdef DEBUG
void JS_FASTCALL AssertArgumentTypes(VMFrame &f);
void JS_FASTCALL TypeCheckPushed(VMFrame &f);
void JS_FASTCALL TypeCheckPopped(VMFrame &f, int32 which);
#endif
void JS_FASTCALL MissedBoundsCheckEntry(VMFrame &f);