Bug 773549 - Implemented inlined calls to specialized DOM methods. (r=sstangl)

This commit is contained in:
Eric Faust 2012-08-08 17:02:46 -07:00
parent f086d02bd6
commit 0c664efd4e
7 changed files with 286 additions and 27 deletions

View File

@ -551,6 +551,101 @@ CodeGenerator::visitCallNative(LCallNative *call)
return true;
}
bool
CodeGenerator::visitCallDOMNative(LCallDOMNative *call)
{
JSFunction *target = call->func();
JS_ASSERT(target);
JS_ASSERT(target->isNative());
JS_ASSERT(target->jitInfo());
JS_ASSERT(call->mir()->isDOMFunction());
int callargslot = call->argslot();
int unusedStack = StackOffsetOfPassedArg(callargslot);
// Registers used for callWithABI() argument-passing.
const Register argJSContext = ToRegister(call->getArgJSContext());
const Register argObj = ToRegister(call->getArgObj());
const Register argPrivate = ToRegister(call->getArgPrivate());
const Register argArgc = ToRegister(call->getArgArgc());
const Register argVp = ToRegister(call->getArgVp());
DebugOnly<uint32> initialStack = masm.framePushed();
masm.checkStackAlignment();
// DOM methods have the signature:
// bool (*)(JSContext *, HandleObject, void *private, unsigned argc, Value *vp)
// Where vp[0] is space for an outparam and the callee, vp[1] is |this|, and vp[2] onward
// are the function arguments.
// Nestle the stack up against the pushed arguments, leaving StackPointer at
// &vp[1]
masm.adjustStack(unusedStack);
masm.movePtr(StackPointer, argObj);
// Push a Value containing the callee object: natives are allowed to access their callee before
// setitng the return value. The StackPointer is moved to &vp[0].
masm.Push(ObjectValue(*target));
masm.movePtr(StackPointer, argVp);
// Use argArgc as scratch.
Register obj = masm.extractObject(Address(argObj, 0), argArgc);
// GetReservedSlot(obj, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate()
masm.loadPrivate(Address(obj, JSObject::getFixedSlotOffset(0)), argPrivate);
// Load argc from the call instruction.
masm.move32(Imm32(call->numStackArgs()), argArgc);
// Push argument into what will become the IonExitFrame
masm.Push(argArgc);
// Construct native exit frame.
uint32 safepointOffset;
if (!masm.buildFakeExitFrame(argJSContext, &safepointOffset))
return false;
masm.enterFakeExitFrame();
if (!markSafepointAt(safepointOffset, call))
return false;
// Construct and execute call.
masm.setupUnalignedABICall(5, argJSContext);
masm.loadJSContext(argJSContext);
masm.passABIArg(argJSContext);
masm.passABIArg(argObj);
masm.passABIArg(argPrivate);
masm.passABIArg(argArgc);
masm.passABIArg(argVp);
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->jitInfo()->op));
// Test for failure.
Label success, exception;
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, &exception);
// Load the outparam vp[0] into output register(s).
masm.loadValue(Address(StackPointer, IonNativeExitFrameLayout::offsetOfResult()), JSReturnOperand);
masm.jump(&success);
// Handle exception case.
{
masm.bind(&exception);
masm.handleException();
}
masm.bind(&success);
// The next instruction is removing the footer of the exit frame, so there
// is no need for leaveFakeExitFrame.
// Move the StackPointer back to its original location, unwinding the native exit frame.
masm.adjustStack(IonNativeExitFrameLayout::Size() - unusedStack);
JS_ASSERT(masm.framePushed() == initialStack);
return true;
}
bool
CodeGenerator::emitCallInvokeFunction(LCallGeneric *call, uint32 unusedStack)
{

View File

@ -164,6 +164,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitProfilingExit(LProfilingExit *lir);
bool visitGetDOMProperty(LGetDOMProperty *lir);
bool visitSetDOMProperty(LSetDOMProperty *lir);
bool visitCallDOMNative(LCallDOMNative *lir);
bool visitCheckOverRecursed(LCheckOverRecursed *lir);
bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);

View File

@ -3737,7 +3737,11 @@ IonBuilder::makeCallBarrier(HandleFunction target, uint32 argc,
// Pass |this| and function.
call->addArg(0, thisArg);
call->initFunction(current->pop());
MDefinition *fun = current->pop();
if (fun->isDOMFunction())
call->setDOMFunction();
call->initFunction(fun);
current->add(call);
current->push(call);
@ -4342,28 +4346,45 @@ TestSingletonPropertyTypes(JSContext *cx, types::TypeSet *types,
break;
case JSVAL_TYPE_OBJECT:
case JSVAL_TYPE_UNKNOWN:
case JSVAL_TYPE_UNKNOWN: {
// For property accesses which may be on many objects, we just need to
// find a prototype common to all the objects; if that prototype
// has the property, the access will not be on a missing property.
if (types->getObjectCount() == 1) {
types::TypeObject *object = types->getTypeObject(0);
if (!object)
return true;
if (object && object->proto) {
if (!TestSingletonProperty(cx, object->proto, id, isKnownConstant))
return false;
if (*isKnownConstant) {
types->addFreeze(cx);
// has the singleton property, the access will not be on a missing property.
bool thoughtConstant = true;
for (unsigned i = 0; i < types->getObjectCount(); i++) {
types::TypeObject *object = types->getTypeObject(i);
if (!object) {
// Try to get it through the singleton.
JSObject *curObj = types->getSingleObject(i);
// As per the comment in jsinfer.h, there can be holes in
// TypeSets, so just skip over them.
if (!curObj)
continue;
object = curObj->getType(cx);
}
// If this is not a known object, a test will be needed.
*testObject = (type != JSVAL_TYPE_OBJECT);
}
return true;
if (object->proto) {
// Test this type.
if (!TestSingletonProperty(cx, object->proto, id, &thoughtConstant))
return false;
// Short circuit
if (!thoughtConstant)
break;
} else {
// Can't be on the prototype chain with no prototypes...
thoughtConstant = false;
break;
}
}
return true;
if (thoughtConstant) {
types->addFreeze(cx);
// If this is not a known object, a test will be needed.
*testObject = (type != JSVAL_TYPE_OBJECT);
}
*isKnownConstant = thoughtConstant;
return true;
}
default:
return true;
}
@ -5435,6 +5456,68 @@ TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func)
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::TypeSet *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::TypeSet::HasObjectFlags(cx, curType,
types::OBJECT_FLAG_NON_DOM);
JS_ASSERT(!wasntDOM);
}
inTypes->addFreeze(cx);
}
bool
IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
types::TypeSet *objTypes, types::TypeSet *pushedTypes)
@ -5641,7 +5724,19 @@ IonBuilder::jsop_getprop(HandlePropertyName name)
MGuardObject *guard = MGuardObject::New(obj);
current->add(guard);
}
return pushConstant(ObjectValue(*singleton));
MConstant *known = MConstant::New(ObjectValue(*singleton));
current->add(known);
current->push(known);
if (singleton->isFunction()) {
RootedFunction singletonFunc(cx, singleton->toFunction());
if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc))
{
FreezeDOMTypes(cx, unaryTypes.inTypes);
known->setDOMFunction();
}
}
return true;
}
}

View File

@ -628,6 +628,59 @@ class LCallNative : public LCallInstructionHelper<BOX_PIECES, 0, 4>
}
};
class LCallDOMNative : public LCallInstructionHelper<BOX_PIECES, 0, 5>
{
uint32 argslot_;
public:
LIR_HEADER(CallDOMNative);
LCallDOMNative(uint32 argslot,
const LDefinition &argJSContext, const LDefinition &argObj,
const LDefinition &argPrivate, const LDefinition &argArgc,
const LDefinition &argVp)
: argslot_(argslot)
{
setTemp(0, argJSContext);
setTemp(1, argObj);
setTemp(2, argPrivate);
setTemp(3, argArgc);
setTemp(4, argVp);
}
JSFunction *func() const {
return mir()->getSingleTarget();
}
uint32 argslot() const {
return argslot_;
}
MCall *mir() const {
return mir_->toCall();
}
// Named for consistency with LCallNative. Actually should be argc().
uint32 numStackArgs() const {
JS_ASSERT(mir()->numStackArgs() >= 1);
return mir()->numStackArgs() - 1;
}
const LAllocation *getArgJSContext() {
return getTemp(0)->output();
}
const LAllocation *getArgObj() {
return getTemp(1)->output();
}
const LAllocation *getArgPrivate() {
return getTemp(2)->output();
}
const LAllocation *getArgArgc() {
return getTemp(3)->output();
}
const LAllocation *getArgVp() {
return getTemp(4)->output();
}
};
// Generates a polymorphic callsite for |new|, where |this| has not been
// pre-allocated by the caller.
class LCallConstructor : public LCallInstructionHelper<BOX_PIECES, 1, 0>

View File

@ -161,7 +161,8 @@
_(ProfilingEnter) \
_(ProfilingExit) \
_(GetDOMProperty) \
_(SetDOMProperty)
_(SetDOMProperty) \
_(CallDOMNative)
#if defined(JS_CPU_X86)
# include "x86/LOpcodes-x86.h"

View File

@ -226,13 +226,26 @@ LIRGenerator::visitCall(MCall *call)
JSFunction *target = call->getSingleTarget();
if (target && target->isNative()) {
LCallNative *lir = new LCallNative(argslot, tempFixed(CallTempReg0),
tempFixed(CallTempReg1), tempFixed(CallTempReg2), tempFixed(CallTempReg3));
if (!defineReturn(lir, call))
return false;
if (!assignSafepoint(lir, call))
return false;
if (call->isDOMFunction()) {
LCallDOMNative *lir = new LCallDOMNative(argslot,
tempFixed(CallTempReg0),
tempFixed(CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
if (!defineReturn(lir, call))
return false;
if (!assignSafepoint(lir, call))
return false;
} else {
LCallNative *lir = new LCallNative(argslot, tempFixed(CallTempReg0),
tempFixed(CallTempReg1), tempFixed(CallTempReg2),
tempFixed(CallTempReg3));
if (!defineReturn(lir, call))
return false;
if (!assignSafepoint(lir, call))
return false;
}
} else if (!target && call->isConstructing()) {
LCallConstructor *lir = new LCallConstructor(useFixed(call->getFunction(), CallTempReg0),
argslot);

View File

@ -50,7 +50,8 @@ MIRType MIRTypeFromValue(const js::Value &vp)
/* The instruction has been marked dead for lazy removal from resume
* points.
*/ \
_(Unused)
_(Unused) \
_(DOMFunction) /* Contains or uses a common DOM method function */
class MDefinition;
class MInstruction;