Bug 928928 - Add missing property GetProp stub in BaselineJIT. r=nbp

This commit is contained in:
Kannan Vijayan 2014-06-27 13:35:24 -04:00
parent 5c5f844ec8
commit 82b1245b8d
4 changed files with 1418 additions and 1 deletions

View File

@ -0,0 +1,78 @@
// This code generates the test cases jit-test/tests/baseline/no-such-property-getprop.js
//
// In particular, it generates the testChain_<N>_<I>() and runChain_<N>_<I>() functions
// at the tail of the file.
var TEST_CASE_FUNCS = [];
function runChain_NNNN_DDDD(obj) {
var sum = 0;
for (var i = 0; i < 100; i++)
sum += obj.foo;
return sum;
}
function testChain_NNNN_DDDD() {
var obj = createTower(NNNN);
assertEq(runChain_NNNN_DDDD(obj), NaN);
updateChain(obj, DDDD, 'foo', 9);
assertEq(runChain_NNNN_DDDD(obj), 900);
}
function generateTestCase(n, d) {
var runFuncName = "runChain_" + n + "_" + d;
var testFuncName = "testChain_" + n + "_" + d;
TEST_CASE_FUNCS.push(testFuncName);
print("//// Test chain of length " + n + " with late-property-addition at depth " + d);
print(uneval(runChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
print(uneval(testChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
print("");
}
// Helper function to create an object with a proto-chain of length N.
function createTower(n) {
var result = Object.create(null);
for (var i = 0; i < n; i++)
result = Object.create(result);
return result;
}
function updateChain(obj, depth, prop, value) {
// Walk down the proto chain |depth| iterations and set |prop| to |value|.
var cur = obj;
for (var i = 0; i < depth; i++)
cur = Object.getPrototypeOf(cur);
var desc = {value:value, writable:true, configurable:true, enumerable:true};
Object.defineProperty(cur, prop, desc);
}
print("/////////////////////////////////////////");
print("// This is a generated file!");
print("// See jit-tests/etc/generate-nosuchproperty-tests.js for the code");
print("// that generated this code!");
print("/////////////////////////////////////////");
print("");
print("/////////////////////////////////////////");
print("// PRELUDE //");
print("/////////////////////////////////////////");
print("");
print(createTower);
print(updateChain);
print("");
print("/////////////////////////////////////////");
print("// TEST CASES //");
print("/////////////////////////////////////////");
print("");
for (var n = 0; n <= 10; n++) {
for (var d = 0; d <= n; d++) {
generateTestCase(n, d);
}
}
print("");
print("/////////////////////////////////////////");
print("// RUNNER //");
print("/////////////////////////////////////////");
print("");
for (var i = 0; i < TEST_CASE_FUNCS.length; i++)
print(TEST_CASE_FUNCS[i] + "();");

File diff suppressed because it is too large Load Diff

View File

@ -335,6 +335,23 @@ ICStub::trace(JSTracer *trc)
MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
break;
}
case ICStub::GetProp_NativeDoesNotExist: {
ICGetProp_NativeDoesNotExist *propStub = toGetProp_NativeDoesNotExist();
JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
switch (propStub->protoChainDepth()) {
case 0: propStub->toImpl<0>()->traceShapes(trc); break;
case 1: propStub->toImpl<1>()->traceShapes(trc); break;
case 2: propStub->toImpl<2>()->traceShapes(trc); break;
case 3: propStub->toImpl<3>()->traceShapes(trc); break;
case 4: propStub->toImpl<4>()->traceShapes(trc); break;
case 5: propStub->toImpl<5>()->traceShapes(trc); break;
case 6: propStub->toImpl<6>()->traceShapes(trc); break;
case 7: propStub->toImpl<7>()->traceShapes(trc); break;
case 8: propStub->toImpl<8>()->traceShapes(trc); break;
default: MOZ_ASSUME_UNREACHABLE("Invalid proto stub.");
}
break;
}
case ICStub::GetProp_CallDOMProxyNative:
case ICStub::GetProp_CallDOMProxyWithGenerationNative: {
ICGetPropCallDOMProxyNativeStub *propStub;
@ -3340,6 +3357,35 @@ EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName n
return true;
}
static bool
CheckHasNoSuchProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
MutableHandleObject lastProto, size_t *protoChainDepthOut)
{
MOZ_ASSERT(protoChainDepthOut != nullptr);
size_t depth = 0;
RootedObject curObj(cx, obj);
while (curObj) {
if (!curObj->isNative())
return false;
Shape *shape = curObj->nativeLookup(cx, NameToId(name));
if (shape)
return false;
JSObject *proto = curObj->getTaggedProto().toObjectOrNull();
if (!proto)
break;
curObj = proto;
depth++;
}
lastProto.set(curObj);
*protoChainDepthOut = depth;
return true;
}
static bool
IsCacheableProtoChain(JSObject *obj, JSObject *holder, bool isDOMProxy=false)
{
@ -6335,6 +6381,48 @@ TryAttachPrimitiveGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc
return true;
}
static bool
TryAttachNativeDoesNotExistStub(JSContext *cx, HandleScript script, jsbytecode *pc,
ICGetProp_Fallback *stub, HandlePropertyName name,
HandleValue val, bool *attached)
{
MOZ_ASSERT(!*attached);
if (!val.isObject())
return true;
RootedObject obj(cx, &val.toObject());
// Don't attach stubs for CALLPROP since those need NoSuchMethod handling.
if (JSOp(*pc) == JSOP_CALLPROP)
return true;
// Check if does-not-exist can be confirmed on property.
RootedObject lastProto(cx);
size_t protoChainDepth = SIZE_MAX;
if (!CheckHasNoSuchProperty(cx, obj, name, &lastProto, &protoChainDepth))
return true;
MOZ_ASSERT(protoChainDepth < SIZE_MAX);
if (protoChainDepth > ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH)
return true;
ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
RootedShape objShape(cx, obj->lastProperty());
RootedShape lastShape(cx, lastProto->lastProperty());
// Confirmed no-such-property. Add stub.
IonSpew(IonSpew_BaselineIC, " Generating GetProp_NativeDoesNotExist stub");
ICGetPropNativeDoesNotExistCompiler compiler(cx, monitorStub, obj, protoChainDepth);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
if (!newStub)
return false;
stub->addNewStub(newStub);
*attached = true;
return true;
}
static bool
DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_,
MutableHandleValue val, MutableHandleValue res)
@ -6423,6 +6511,14 @@ DoGetPropFallback(JSContext *cx, BaselineFrame *frame, ICGetProp_Fallback *stub_
return true;
}
if (res.isUndefined()) {
// Try attaching property-not-found optimized stub for undefined results.
if (!TryAttachNativeDoesNotExistStub(cx, script, pc, stub, name, val, &attached))
return false;
if (attached)
return true;
}
JS_ASSERT(!attached);
stub->noteUnoptimizableAccess();
@ -6680,6 +6776,87 @@ ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
return true;
}
ICStub *
ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace *space)
{
AutoShapeVector shapes(cx);
if (!shapes.append(obj_->lastProperty()))
return nullptr;
if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
return nullptr;
JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
ICStub *stub = nullptr;
switch(protoChainDepth_) {
case 0: stub = getStubSpecific<0>(space, &shapes); break;
case 1: stub = getStubSpecific<1>(space, &shapes); break;
case 2: stub = getStubSpecific<2>(space, &shapes); break;
case 3: stub = getStubSpecific<3>(space, &shapes); break;
case 4: stub = getStubSpecific<4>(space, &shapes); break;
case 5: stub = getStubSpecific<5>(space, &shapes); break;
case 6: stub = getStubSpecific<6>(space, &shapes); break;
case 7: stub = getStubSpecific<7>(space, &shapes); break;
case 8: stub = getStubSpecific<8>(space, &shapes); break;
default: MOZ_ASSUME_UNREACHABLE("ProtoChainDepth too high.");
}
if (!stub)
return nullptr;
return stub;
}
bool
ICGetPropNativeDoesNotExistCompiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
GeneralRegisterSet regs(availableGeneralRegs(1));
Register scratch = regs.takeAny();
#ifdef DEBUG
// Ensure that protoChainDepth_ matches the protoChainDepth stored on the stub.
{
Label ok;
masm.load16ZeroExtend(Address(BaselineStubReg, ICStub::offsetOfExtra()), scratch);
masm.branch32(Assembler::Equal, scratch, Imm32(protoChainDepth_), &ok);
masm.assumeUnreachable("Non-matching proto chain depth on stub.");
masm.bind(&ok);
}
#endif // DEBUG
// Guard input is an object.
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
// Unbox and guard against old shape.
Register objReg = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativeDoesNotExist::offsetOfShape(0)),
scratch);
masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
Register protoReg = regs.takeAny();
// Check the proto chain.
for (size_t i = 0; i < protoChainDepth_; i++) {
masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failure);
size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i + 1);
masm.loadPtr(Address(BaselineStubReg, shapeOffset), scratch);
masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failure);
}
// Shape and type checks succeeded, ok to proceed.
masm.moveValue(UndefinedValue(), R0);
// Normally for this op, the result would have to be monitored by TI.
// However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
// that undefined is already registered with the type-set, this can be avoided.
EmitReturnFromIC(masm);
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
bool
ICGetProp_CallScripted::Compiler::generateStubCode(MacroAssembler &masm)
{
@ -10125,6 +10302,43 @@ ICGetProp_NativePrototype::Clone(JSContext *cx, ICStubSpace *space, ICStub *firs
holder, holderShape);
}
ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist(
JitCode *stubCode, ICStub *firstMonitorStub, size_t protoChainDepth)
: ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub)
{
MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
extra_ = protoChainDepth;
}
/* static */ size_t
ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx)
{
MOZ_ASSERT(ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx) ==
ICGetProp_NativeDoesNotExistImpl<
ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx));
return ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx);
}
template <size_t ProtoChainDepth>
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::ICGetProp_NativeDoesNotExistImpl(
JitCode *stubCode, ICStub *firstMonitorStub, const AutoShapeVector *shapes)
: ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, ProtoChainDepth)
{
MOZ_ASSERT(shapes->length() == NumShapes);
for (size_t i = 0; i < NumShapes; i++)
shapes_[i].init((*shapes)[i]);
}
ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler(
JSContext *cx, ICStub *firstMonitorStub, HandleObject obj, size_t protoChainDepth)
: ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist),
firstMonitorStub_(firstMonitorStub),
obj_(cx, obj),
protoChainDepth_(protoChainDepth)
{
MOZ_ASSERT(protoChainDepth_ <= ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH);
}
ICGetPropCallGetter::ICGetPropCallGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
HandleObject holder, HandleShape holderShape, HandleFunction getter,
uint32_t pcOffset)

View File

@ -414,6 +414,7 @@ class ICEntry
_(GetProp_Primitive) \
_(GetProp_StringLength) \
_(GetProp_Native) \
_(GetProp_NativeDoesNotExist) \
_(GetProp_NativePrototype) \
_(GetProp_CallScripted) \
_(GetProp_CallNative) \
@ -4412,6 +4413,104 @@ class ICGetPropNativeCompiler : public ICStubCompiler
}
};
template <size_t ProtoChainDepth> class ICGetProp_NativeDoesNotExistImpl;
class ICGetProp_NativeDoesNotExist : public ICMonitoredStub
{
friend class ICStubSpace;
public:
static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
protected:
ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub,
size_t protoChainDepth);
public:
static inline ICGetProp_NativeDoesNotExist *New(ICStubSpace *space, JitCode *code,
ICStub *firstMonitorStub,
size_t protoChainDepth)
{
if (!code)
return nullptr;
return space->allocate<ICGetProp_NativeDoesNotExist>(code, firstMonitorStub,
protoChainDepth);
}
size_t protoChainDepth() const {
MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
return extra_;
}
template <size_t ProtoChainDepth>
ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *toImpl() {
MOZ_ASSERT(ProtoChainDepth == protoChainDepth());
return static_cast<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *>(this);
}
static size_t offsetOfShape(size_t idx);
};
template <size_t ProtoChainDepth>
class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist
{
friend class ICStubSpace;
public:
static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
static const size_t NumShapes = ProtoChainDepth + 1;
private:
mozilla::Array<HeapPtrShape, NumShapes> shapes_;
ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub,
const AutoShapeVector *shapes);
public:
static inline ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *New(
ICStubSpace *space, JitCode *code, ICStub *firstMonitorStub,
const AutoShapeVector *shapes)
{
if (!code)
return nullptr;
return space->allocate<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>>(
code, firstMonitorStub, shapes);
}
void traceShapes(JSTracer *trc) {
for (size_t i = 0; i < NumShapes; i++)
MarkShape(trc, &shapes_[i], "baseline-getpropnativedoesnotexist-stub-shape");
}
static size_t offsetOfShape(size_t idx) {
return offsetof(ICGetProp_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape));
}
};
class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
{
ICStub *firstMonitorStub_;
RootedObject obj_;
size_t protoChainDepth_;
protected:
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
}
bool generateStubCode(MacroAssembler &masm);
public:
ICGetPropNativeDoesNotExistCompiler(JSContext *cx, ICStub *firstMonitorStub,
HandleObject obj, size_t protoChainDepth);
template <size_t ProtoChainDepth>
ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
return ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::New(space, getStubCode(),
firstMonitorStub_, shapes);
}
ICStub *getStub(ICStubSpace *space);
};
class ICGetPropCallGetter : public ICMonitoredStub
{
friend class ICStubSpace;
@ -5155,7 +5254,8 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
}
};
class ICSetPropNativeAddCompiler : public ICStubCompiler {
class ICSetPropNativeAddCompiler : public ICStubCompiler
{
RootedObject obj_;
RootedShape oldShape_;
size_t protoChainDepth_;