mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
[INFER] Add jitcode assertions for type correctness around property accesses, bug 685186.
This commit is contained in:
parent
72e409987d
commit
0d13c1627d
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user