mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 773549 - Implemented inlined calls to specialized DOM methods. (r=sstangl)
This commit is contained in:
parent
f086d02bd6
commit
0c664efd4e
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -161,7 +161,8 @@
|
||||
_(ProfilingEnter) \
|
||||
_(ProfilingExit) \
|
||||
_(GetDOMProperty) \
|
||||
_(SetDOMProperty)
|
||||
_(SetDOMProperty) \
|
||||
_(CallDOMNative)
|
||||
|
||||
#if defined(JS_CPU_X86)
|
||||
# include "x86/LOpcodes-x86.h"
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user