Bug 822340. Make the Ion optimization for DOM method calls sound. r=jandem

The static functions just got moved with no changes made to them
except for a change from inTypes->unknown() to
inTypes->unknownObject() in the first test in TestAreKnownDOMTypes,
becase the rest of the method depends on the stronger condition, and
it was being ensured accidentally before.
This commit is contained in:
Boris Zbarsky 2012-12-19 17:47:39 -08:00
parent 51f20b1833
commit 4d4f84e2ba
4 changed files with 131 additions and 108 deletions

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<script>
var xhr = new XMLHttpRequest;
function f() {
var x = xhr.getResponseHeader;
x("abc");
}
for (var i = 0; i < 20000; ++i) {
try { f(); } catch (e) {}
}
</script>

View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<script>
var l = document.getElementsByTagName("*");
var count = 20000;
for (var i = 0; i < count; ++i) {
l.item(0);
}
</script>

View File

@ -1 +1,3 @@
asserts-if(cocoaWidget,0-1) load 769464.html
load 822340-1.html
load 822340-2.html

View File

@ -4109,6 +4109,103 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
return makeCallBarrier(target, argc, constructing, types, barrier);
}
static bool
TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func,
JSJitInfo::OpType opType)
{
if (!func->isNative() || !func->jitInfo())
return false;
// If all the DOM objects flowing through are legal with this
// property, we can bake in a call to the bottom half of the DOM
// accessor
DOMInstanceClassMatchesProto instanceChecker =
GetDOMCallbacks(cx->runtime)->instanceClassMatchesProto;
const JSJitInfo *jinfo = func->jitInfo();
if (jinfo->type != opType)
return false;
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
if (!curObj)
continue;
curType = curObj->getType(cx);
}
JSObject *typeProto = curType->proto;
RootedObject proto(cx, typeProto);
if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
return false;
}
return true;
}
static bool
TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
{
if (inTypes->unknownObject())
return false;
// First iterate to make sure they all are DOM objects, then freeze all of
// them as such if they are.
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
// Skip holes in TypeSets.
if (!curObj)
continue;
curType = curObj->getType(cx);
}
if (curType->unknownProperties())
return false;
// Unlike TypeSet::HasObjectFlags, TypeObject::hasAnyFlags doesn't add a
// freeze.
if (curType->hasAnyFlags(types::OBJECT_FLAG_NON_DOM))
return false;
}
// If we didn't check anything, no reason to say yes.
if (inTypes->getObjectCount() > 0)
return true;
return false;
}
static void
FreezeDOMTypes(JSContext *cx, types::StackTypeSet *inTypes)
{
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
// Skip holes in TypeSets.
if (!curObj)
continue;
curType = curObj->getType(cx);
}
// Add freeze by asking the question.
DebugOnly<bool> wasntDOM =
types::HeapTypeSet::HasObjectFlags(cx, curType, types::OBJECT_FLAG_NON_DOM);
JS_ASSERT(!wasntDOM);
}
}
MCall *
IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructing)
{
@ -4170,9 +4267,20 @@ IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructi
// Pass |this| and function.
call->addArg(0, thisArg);
if (target && JSOp(*pc) == JSOP_CALL) {
// We know we have a single call target. Check whether the "this" types
// are DOM types and our function a DOM function, and if so flag the
// MCall accordingly.
types::StackTypeSet *thisTypes = oracle->getCallArg(script(), argc, 0, pc);
if (thisTypes &&
TestAreKnownDOMTypes(cx, thisTypes) &&
TestShouldDOMCall(cx, thisTypes, target, JSJitInfo::Method))
{
FreezeDOMTypes(cx, thisTypes);
call->setDOMFunction();
}
}
MDefinition *fun = current->pop();
if (fun->isDOMFunction())
call->setDOMFunction();
call->initFunction(fun);
current->add(call);
@ -5950,103 +6058,6 @@ IonBuilder::TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types, Handle
return true;
}
static bool
TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func,
JSJitInfo::OpType opType)
{
if (!func->isNative() || !func->jitInfo())
return false;
// If all the DOM objects flowing through are legal with this
// property, we can bake in a call to the bottom half of the DOM
// accessor
DOMInstanceClassMatchesProto instanceChecker =
GetDOMCallbacks(cx->runtime)->instanceClassMatchesProto;
const JSJitInfo *jinfo = func->jitInfo();
if (jinfo->type != opType)
return false;
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
if (!curObj)
continue;
curType = curObj->getType(cx);
}
JSObject *typeProto = curType->proto;
RootedObject proto(cx, typeProto);
if (!instanceChecker(proto, jinfo->protoID, jinfo->depth))
return false;
}
return true;
}
static bool
TestAreKnownDOMTypes(JSContext *cx, types::TypeSet *inTypes)
{
if (inTypes->unknown())
return false;
// First iterate to make sure they all are DOM objects, then freeze all of
// them as such if they are.
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
// Skip holes in TypeSets.
if (!curObj)
continue;
curType = curObj->getType(cx);
}
if (curType->unknownProperties())
return false;
// Unlike TypeSet::HasObjectFlags, TypeObject::hasAnyFlags doesn't add a
// freeze.
if (curType->hasAnyFlags(types::OBJECT_FLAG_NON_DOM))
return false;
}
// If we didn't check anything, no reason to say yes.
if (inTypes->getObjectCount() > 0)
return true;
return false;
}
static void
FreezeDOMTypes(JSContext *cx, types::StackTypeSet *inTypes)
{
for (unsigned i = 0; i < inTypes->getObjectCount(); i++) {
types::TypeObject *curType = inTypes->getTypeObject(i);
if (!curType) {
JSObject *curObj = inTypes->getSingleObject(i);
// Skip holes in TypeSets.
if (!curObj)
continue;
curType = curObj->getType(cx);
}
// Add freeze by asking the question.
DebugOnly<bool> wasntDOM =
types::HeapTypeSet::HasObjectFlags(cx, curType, types::OBJECT_FLAG_NON_DOM);
JS_ASSERT(!wasntDOM);
}
}
bool
IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
types::StackTypeSet *objTypes, types::StackTypeSet *pushedTypes)
@ -6311,15 +6322,6 @@ IonBuilder::getPropTryConstant(bool *emitted, HandleId id, types::StackTypeSet *
obj->setFoldedUnchecked();
MConstant *known = MConstant::New(ObjectValue(*singleton));
if (singleton->isFunction()) {
RootedFunction singletonFunc(cx, singleton->toFunction());
if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc, JSJitInfo::Method))
{
FreezeDOMTypes(cx, unaryTypes.inTypes);
known->setDOMFunction();
}
}
current->add(known);
current->push(known);