зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1824051 - Handle IC for scripted proxy get in Ion r=iain,jandem
Differential Revision: https://phabricator.services.mozilla.com/D182133
This commit is contained in:
Родитель
e77934e39a
Коммит
4a0b1e43c4
|
@ -9592,6 +9592,8 @@ void CacheIRCompiler::callVMInternal(MacroAssembler& masm, VMFunctionId id) {
|
|||
sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
|
||||
masm.implicitPop(frameSize + framePop);
|
||||
|
||||
masm.freeStack(asIon()->localTracingSlots() * sizeof(Value));
|
||||
|
||||
// Pop IonICCallFrameLayout.
|
||||
masm.Pop(FramePointer);
|
||||
masm.freeStack(IonICCallFrameLayout::Size() - sizeof(void*));
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "js/RegExpFlags.h" // JS::RegExpFlag
|
||||
#include "js/ScalarType.h" // js::Scalar::Type
|
||||
#include "proxy/DOMProxy.h"
|
||||
#include "proxy/ScriptedProxyHandler.h"
|
||||
#include "util/CheckedArithmetic.h"
|
||||
#include "util/Unicode.h"
|
||||
#include "vm/ArrayBufferViewObject.h"
|
||||
|
@ -4588,6 +4589,18 @@ void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) {
|
|||
bailoutFrom(&bail, guard->snapshot());
|
||||
}
|
||||
|
||||
void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
|
||||
Register obj = ToRegister(guard->input());
|
||||
|
||||
Label bail;
|
||||
|
||||
Address handlerAddr(obj, ProxyObject::offsetOfHandler());
|
||||
masm.branchPtr(Assembler::NotEqual, handlerAddr,
|
||||
ImmPtr(guard->mir()->handler()), &bail);
|
||||
|
||||
bailoutFrom(&bail, guard->snapshot());
|
||||
}
|
||||
|
||||
void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
|
||||
Register input = ToRegister(guard->input());
|
||||
Register expected = ToRegister(guard->expected());
|
||||
|
@ -14536,6 +14549,77 @@ void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) {
|
|||
masm.bind(&done);
|
||||
}
|
||||
|
||||
void CodeGenerator::visitLoadScriptedProxyHandler(
|
||||
LLoadScriptedProxyHandler* ins) {
|
||||
const Register obj = ToRegister(ins->getOperand(0));
|
||||
Register output = ToRegister(ins->output());
|
||||
|
||||
masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output);
|
||||
masm.unboxObject(Address(output, js::detail::ProxyReservedSlots::offsetOfSlot(
|
||||
ScriptedProxyHandler::HANDLER_EXTRA)),
|
||||
output);
|
||||
}
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
void CodeGenerator::visitCheckScriptedProxyGetResult(
|
||||
LCheckScriptedProxyGetResult* ins) {
|
||||
ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex);
|
||||
ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex);
|
||||
ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex);
|
||||
Register scratch = ToRegister(ins->temp0());
|
||||
Register scratch2 = ToRegister(ins->temp1());
|
||||
|
||||
using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
|
||||
MutableHandleValue);
|
||||
OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>(
|
||||
ins, ArgList(scratch, id, value), StoreValueTo(value));
|
||||
|
||||
masm.unboxObject(target, scratch);
|
||||
masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
|
||||
scratch2, ool->entry());
|
||||
masm.bind(ool->rejoin());
|
||||
}
|
||||
#endif
|
||||
|
||||
void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) {
|
||||
ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex);
|
||||
ValueOperand output = ToOutValue(ins);
|
||||
Register scratch = ToRegister(ins->temp0());
|
||||
|
||||
masm.moveValue(id, output);
|
||||
|
||||
Label done, callVM;
|
||||
Maybe<Label> bail;
|
||||
|
||||
MDefinition* idDef = ins->mir()->idVal();
|
||||
if (idDef->isBox()) {
|
||||
idDef = idDef->toBox()->input();
|
||||
}
|
||||
if (idDef->type() != MIRType::Int32) {
|
||||
bail.emplace();
|
||||
ScratchTagScope tag(masm, output);
|
||||
masm.splitTagForTest(output, tag);
|
||||
masm.branchTestString(Assembler::Equal, tag, &done);
|
||||
masm.branchTestSymbol(Assembler::Equal, tag, &done);
|
||||
masm.branchTestInt32(Assembler::NotEqual, tag, &*bail);
|
||||
}
|
||||
|
||||
masm.unboxInt32(output, scratch);
|
||||
|
||||
using Fn = JSLinearString* (*)(JSContext*, int);
|
||||
OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
|
||||
ins, ArgList(scratch), StoreRegisterTo(output.scratchReg()));
|
||||
|
||||
emitIntToString(scratch, output.scratchReg(), ool->entry());
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
|
||||
masm.bind(&done);
|
||||
if (bail) {
|
||||
bailoutFrom(&*bail, ins->snapshot());
|
||||
}
|
||||
}
|
||||
|
||||
void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) {
|
||||
const Register obj = ToRegister(ins->getOperand(0));
|
||||
size_t slot = ins->mir()->slot();
|
||||
|
|
|
@ -53,7 +53,8 @@ IonCacheIRCompiler::IonCacheIRCompiler(JSContext* cx, TempAllocator& alloc,
|
|||
writer_(writer),
|
||||
ic_(ic),
|
||||
ionScript_(ionScript),
|
||||
savedLiveRegs_(false) {
|
||||
savedLiveRegs_(false),
|
||||
localTracingSlots_(0) {
|
||||
MOZ_ASSERT(ic_);
|
||||
MOZ_ASSERT(ionScript_);
|
||||
}
|
||||
|
@ -272,6 +273,22 @@ void IonCacheIRCompiler::enterStubFrame(MacroAssembler& masm,
|
|||
enteredStubFrame_ = true;
|
||||
}
|
||||
|
||||
void IonCacheIRCompiler::storeTracedValue(MacroAssembler& masm,
|
||||
ValueOperand value) {
|
||||
MOZ_ASSERT(localTracingSlots_ < 255);
|
||||
masm.Push(value);
|
||||
localTracingSlots_++;
|
||||
}
|
||||
|
||||
void IonCacheIRCompiler::loadTracedValue(MacroAssembler& masm,
|
||||
uint8_t slotIndex,
|
||||
ValueOperand value) {
|
||||
MOZ_ASSERT(slotIndex <= localTracingSlots_);
|
||||
int32_t offset = IonICCallFrameLayout::LocallyTracedValueOffset +
|
||||
slotIndex * sizeof(Value);
|
||||
masm.loadValue(Address(FramePointer, -offset), value);
|
||||
}
|
||||
|
||||
bool IonCacheIRCompiler::init() {
|
||||
if (!allocator.init()) {
|
||||
return false;
|
||||
|
@ -594,6 +611,8 @@ JitCode* IonCacheIRCompiler::compile(IonICStub* stub) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
newStubCode->setLocalTracingSlots(localTracingSlots_);
|
||||
|
||||
for (CodeOffset offset : nextCodeOffsets_) {
|
||||
Assembler::PatchDataWithValueCheck(CodeLocationLabel(newStubCode, offset),
|
||||
ImmPtr(stub->nextCodeRawPtr()),
|
||||
|
@ -922,6 +941,145 @@ bool IonCacheIRCompiler::emitCallScriptedGetterResult(
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
template <typename IdType>
|
||||
bool IonCacheIRCompiler::emitCallScriptedProxyGetShared(ValOperandId targetId,
|
||||
ObjOperandId receiverId,
|
||||
ObjOperandId handlerId,
|
||||
uint32_t trapOffset,
|
||||
IdType id) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
AutoSaveLiveRegisters save(*this);
|
||||
AutoOutputRegister output(*this);
|
||||
|
||||
ValueOperand target = allocator.useValueRegister(masm, targetId);
|
||||
Register receiver = allocator.useRegister(masm, receiverId);
|
||||
Register handler = allocator.useRegister(masm, handlerId);
|
||||
ValueOperand idVal;
|
||||
if constexpr (std::is_same_v<IdType, ValOperandId>) {
|
||||
idVal = allocator.useValueRegister(masm, id);
|
||||
}
|
||||
|
||||
JSFunction* trap = &objectStubField(trapOffset)->as<JSFunction>();
|
||||
AutoScratchRegister scratch(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
ValueOperand scratchVal(scratch);
|
||||
ValueOperand scratchVal2(scratch2);
|
||||
|
||||
allocator.discardStack(masm);
|
||||
|
||||
uint32_t framePushedBefore = masm.framePushed();
|
||||
|
||||
enterStubFrame(masm, save);
|
||||
|
||||
// We need to keep the target around to potentially validate the proxy result
|
||||
storeTracedValue(masm, target);
|
||||
if constexpr (std::is_same_v<IdType, ValOperandId>) {
|
||||
// Same for the id, assuming it's not baked in
|
||||
storeTracedValue(masm, idVal);
|
||||
}
|
||||
uint32_t framePushedBeforeArgs = masm.framePushed();
|
||||
|
||||
// The JitFrameLayout pushed below will be aligned to JitStackAlignment,
|
||||
// so we just have to make sure the stack is aligned after we push the
|
||||
// |this| + argument Values.
|
||||
uint32_t argSize = (std::max(trap->nargs(), (size_t)3) + 1) * sizeof(Value);
|
||||
uint32_t padding =
|
||||
ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
|
||||
MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
|
||||
MOZ_ASSERT(padding < JitStackAlignment);
|
||||
masm.reserveStack(padding);
|
||||
|
||||
for (size_t i = 3; i < trap->nargs(); i++) {
|
||||
masm.Push(UndefinedValue());
|
||||
}
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, receiver, scratchVal);
|
||||
masm.Push(scratchVal);
|
||||
|
||||
if constexpr (std::is_same_v<IdType, ValOperandId>) {
|
||||
masm.Push(idVal);
|
||||
} else {
|
||||
masm.movePropertyKey(idStubField(id), scratch);
|
||||
masm.tagValue(JSVAL_TYPE_STRING, scratch, scratchVal);
|
||||
masm.Push(scratchVal);
|
||||
}
|
||||
|
||||
masm.Push(target);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, handler, scratchVal);
|
||||
masm.Push(scratchVal);
|
||||
|
||||
masm.movePtr(ImmGCPtr(trap), scratch);
|
||||
|
||||
masm.Push(scratch);
|
||||
masm.PushFrameDescriptorForJitCall(FrameType::IonICCall, /* argc = */ 3);
|
||||
|
||||
// Check stack alignment. Add 2 * sizeof(uintptr_t) for the return address and
|
||||
// frame pointer pushed by the call/callee.
|
||||
MOZ_ASSERT(
|
||||
((masm.framePushed() + 2 * sizeof(uintptr_t)) % JitStackAlignment) == 0);
|
||||
|
||||
MOZ_ASSERT(trap->hasJitEntry());
|
||||
masm.loadJitCodeRaw(scratch, scratch);
|
||||
masm.callJit(scratch);
|
||||
|
||||
masm.storeCallResultValue(output);
|
||||
|
||||
Label success, end;
|
||||
loadTracedValue(masm, 0, scratchVal);
|
||||
masm.unboxObject(scratchVal, scratch);
|
||||
masm.branchTestObjectNeedsProxyResultValidation(Assembler::Zero, scratch,
|
||||
scratch2, &success);
|
||||
|
||||
if constexpr (std::is_same_v<IdType, ValOperandId>) {
|
||||
loadTracedValue(masm, 1, scratchVal2);
|
||||
} else {
|
||||
masm.moveValue(StringValue(idStubField(id).toString()), scratchVal2);
|
||||
}
|
||||
|
||||
uint32_t framePushedAfterCall = masm.framePushed();
|
||||
masm.freeStack(masm.framePushed() - framePushedBeforeArgs);
|
||||
|
||||
masm.Push(output.valueReg());
|
||||
masm.Push(scratchVal2);
|
||||
masm.Push(scratch);
|
||||
|
||||
using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
|
||||
MutableHandleValue);
|
||||
callVM<Fn, CheckProxyGetByValueResult>(masm);
|
||||
|
||||
masm.storeCallResultValue(output);
|
||||
|
||||
masm.jump(&end);
|
||||
masm.bind(&success);
|
||||
masm.setFramePushed(framePushedAfterCall);
|
||||
|
||||
// Restore the frame pointer and stack pointer.
|
||||
masm.loadPtr(Address(FramePointer, 0), FramePointer);
|
||||
masm.freeStack(masm.framePushed() - framePushedBefore);
|
||||
masm.bind(&end);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IonCacheIRCompiler::emitCallScriptedProxyGetResult(
|
||||
ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
|
||||
uint32_t trapOffset, uint32_t id, uint32_t nargsAndFlags) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
return emitCallScriptedProxyGetShared(targetId, receiverId, handlerId,
|
||||
trapOffset, id);
|
||||
}
|
||||
|
||||
bool IonCacheIRCompiler::emitCallScriptedProxyGetByValueResult(
|
||||
ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
|
||||
ValOperandId idId, uint32_t trapOffset, uint32_t nargsAndFlags) {
|
||||
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
|
||||
return emitCallScriptedProxyGetShared(targetId, receiverId, handlerId,
|
||||
trapOffset, idId);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IonCacheIRCompiler::emitCallInlinedGetterResult(
|
||||
ValOperandId receiverId, uint32_t getterOffset, uint32_t icScriptOffset,
|
||||
bool sameRealm, uint32_t nargsAndFlagsOffset) {
|
||||
|
|
|
@ -53,6 +53,7 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler {
|
|||
#endif
|
||||
|
||||
IonICPerfSpewer& perfSpewer() { return perfSpewer_; }
|
||||
uint8_t localTracingSlots() const { return localTracingSlots_; }
|
||||
|
||||
private:
|
||||
const CacheIRWriter& writer_;
|
||||
|
@ -64,6 +65,7 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler {
|
|||
mozilla::Maybe<CodeOffset> stubJitCodeOffset_;
|
||||
|
||||
bool savedLiveRegs_;
|
||||
uint8_t localTracingSlots_;
|
||||
|
||||
IonICPerfSpewer perfSpewer_;
|
||||
|
||||
|
@ -74,6 +76,9 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler {
|
|||
T rawInt64StubField(uint32_t offset);
|
||||
|
||||
void enterStubFrame(MacroAssembler& masm, const AutoSaveLiveRegisters&);
|
||||
void storeTracedValue(MacroAssembler& masm, ValueOperand value);
|
||||
void loadTracedValue(MacroAssembler& masm, uint8_t slotIndex,
|
||||
ValueOperand value);
|
||||
|
||||
template <typename Fn, Fn fn>
|
||||
void callVM(MacroAssembler& masm);
|
||||
|
@ -82,6 +87,13 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler {
|
|||
CacheOp op, ObjOperandId objId, uint32_t offsetOffset, ValOperandId rhsId,
|
||||
uint32_t newShapeOffset, mozilla::Maybe<uint32_t> numNewSlotsOffset);
|
||||
|
||||
template <typename IdType>
|
||||
[[nodiscard]] bool emitCallScriptedProxyGetShared(ValOperandId targetId,
|
||||
ObjOperandId receiverId,
|
||||
ObjOperandId handlerId,
|
||||
uint32_t trapOffset,
|
||||
IdType id);
|
||||
|
||||
void pushStubCodePointer();
|
||||
|
||||
CACHE_IR_COMPILER_UNSHARED_GENERATED
|
||||
|
|
|
@ -1145,7 +1145,8 @@ static void TraceIonICCallFrame(JSTracer* trc, const JSJitFrameIter& frame) {
|
|||
TraceRoot(trc, layout->stubCode(), "ion-ic-call-code");
|
||||
|
||||
for (int i = 0; i < (*layout->stubCode())->localTracingSlots(); ++i) {
|
||||
TraceRoot(trc, layout->locallyTracedValuePtr(i), "TODO");
|
||||
TraceRoot(trc, layout->locallyTracedValuePtr(i),
|
||||
"ion-ic-local-tracing-slot");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1976,6 +1976,29 @@
|
|||
num_temps: 1
|
||||
mir_op: ClampToUint8
|
||||
|
||||
- name: LoadScriptedProxyHandler
|
||||
result_type: WordSized
|
||||
operands:
|
||||
object: WordSized
|
||||
mir_op: true
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
- name: CheckScriptedProxyGetResult
|
||||
operands:
|
||||
target: BoxedValue
|
||||
id: BoxedValue
|
||||
value: BoxedValue
|
||||
num_temps: 2
|
||||
mir_op: true
|
||||
#endif
|
||||
|
||||
- name: IdToStringOrSymbol
|
||||
result_type: BoxedValue
|
||||
operands:
|
||||
id: BoxedValue
|
||||
num_temps: 1
|
||||
mir_op: true
|
||||
|
||||
# Load a boxed value from an object's fixed slot.
|
||||
- name: LoadFixedSlotV
|
||||
result_type: BoxedValue
|
||||
|
@ -2696,6 +2719,11 @@
|
|||
object: WordSized
|
||||
num_temps: 1
|
||||
|
||||
- name: GuardHasProxyHandler
|
||||
operands:
|
||||
object: WordSized
|
||||
mir_op: true
|
||||
|
||||
- name: GuardNoDenseElements
|
||||
operands:
|
||||
in: WordSized
|
||||
|
|
|
@ -4422,6 +4422,21 @@ void LIRGenerator::visitStoreTypedArrayElementHole(
|
|||
}
|
||||
}
|
||||
|
||||
void LIRGenerator::visitLoadScriptedProxyHandler(
|
||||
MLoadScriptedProxyHandler* ins) {
|
||||
LLoadScriptedProxyHandler* lir = new (alloc())
|
||||
LLoadScriptedProxyHandler(useRegisterAtStart(ins->object()));
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitIdToStringOrSymbol(MIdToStringOrSymbol* ins) {
|
||||
LIdToStringOrSymbol* lir =
|
||||
new (alloc()) LIdToStringOrSymbol(useBoxAtStart(ins->idVal()), temp());
|
||||
assignSnapshot(lir, ins->bailoutKind());
|
||||
defineBox(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot* ins) {
|
||||
MDefinition* obj = ins->object();
|
||||
MOZ_ASSERT(obj->type() == MIRType::Object);
|
||||
|
@ -4907,6 +4922,15 @@ void LIRGenerator::visitGuardIsTypedArray(MGuardIsTypedArray* ins) {
|
|||
redefine(ins, ins->object());
|
||||
}
|
||||
|
||||
void LIRGenerator::visitGuardHasProxyHandler(MGuardHasProxyHandler* ins) {
|
||||
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
|
||||
|
||||
auto* lir = new (alloc()) LGuardHasProxyHandler(useRegister(ins->object()));
|
||||
assignSnapshot(lir, ins->bailoutKind());
|
||||
add(lir, ins);
|
||||
redefine(ins, ins->object());
|
||||
}
|
||||
|
||||
void LIRGenerator::visitNurseryObject(MNurseryObject* ins) {
|
||||
MOZ_ASSERT(ins->type() == MIRType::Object);
|
||||
|
||||
|
@ -6112,6 +6136,21 @@ void LIRGenerator::visitCheckIsObj(MCheckIsObj* ins) {
|
|||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
void LIRGenerator::visitCheckScriptedProxyGetResult(
|
||||
MCheckScriptedProxyGetResult* ins) {
|
||||
MDefinition* target = ins->target();
|
||||
MDefinition* id = ins->id();
|
||||
MDefinition* value = ins->value();
|
||||
|
||||
LCheckScriptedProxyGetResult* lir =
|
||||
new (alloc()) LCheckScriptedProxyGetResult(useBox(target), useBox(id),
|
||||
useBox(value), temp(), temp());
|
||||
add(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
#endif
|
||||
|
||||
void LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins) {
|
||||
MDefinition* checkVal = ins->checkValue();
|
||||
MOZ_ASSERT(checkVal->type() == MIRType::Value);
|
||||
|
|
|
@ -3416,6 +3416,17 @@ AliasSet MGuardArgumentsObjectFlags::getAliasSet() const {
|
|||
return AliasSet::Load(AliasSet::FixedSlot);
|
||||
}
|
||||
|
||||
MDefinition* MIdToStringOrSymbol::foldsTo(TempAllocator& alloc) {
|
||||
if (idVal()->isBox()) {
|
||||
MIRType idType = idVal()->toBox()->input()->type();
|
||||
if (idType == MIRType::String || idType == MIRType::Symbol) {
|
||||
return idVal();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
MDefinition* MReturnFromCtor::foldsTo(TempAllocator& alloc) {
|
||||
MDefinition* rval = value();
|
||||
if (rval->isBox()) {
|
||||
|
|
|
@ -522,6 +522,31 @@
|
|||
# The flags are packed with the length in a fixed private slot.
|
||||
alias_set: custom
|
||||
|
||||
- name: LoadScriptedProxyHandler
|
||||
operands:
|
||||
object: Object
|
||||
result_type: Object
|
||||
congruent_to: if_operands_equal
|
||||
alias_set: none
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
- name: CheckScriptedProxyGetResult
|
||||
operands:
|
||||
target: Value
|
||||
id: Value
|
||||
value: Value
|
||||
guard: true
|
||||
alias_set: none
|
||||
#endif
|
||||
|
||||
- name: IdToStringOrSymbol
|
||||
operands:
|
||||
idVal: Value
|
||||
result_type: Value
|
||||
congruent_to: if_operands_equal
|
||||
alias_set: none
|
||||
folds_to: custom
|
||||
|
||||
# Given a MIRType::Value A and a MIRType::Object B:
|
||||
# If the Value may be safely unboxed to an Object, return Object(A).
|
||||
# Otherwise, return B.
|
||||
|
@ -1732,6 +1757,17 @@
|
|||
congruent_to: if_operands_equal
|
||||
alias_set: none
|
||||
|
||||
- name: GuardHasProxyHandler
|
||||
operands:
|
||||
object: Object
|
||||
arguments:
|
||||
handler: const void*
|
||||
result_type: Object
|
||||
guard: true
|
||||
movable: true
|
||||
congruent_to: if_operands_equal
|
||||
alias_set: none
|
||||
|
||||
# Loads a specific JSObject* that was originally nursery-allocated.
|
||||
# See also WarpObjectField.
|
||||
- name: NurseryObject
|
||||
|
|
|
@ -1081,6 +1081,7 @@ bool ClampPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const {
|
|||
_(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1>>) \
|
||||
_(MixPolicy<StringPolicy<0>, StringPolicy<1>>) \
|
||||
_(MixPolicy<BoxPolicy<0>, BoxPolicy<1>>) \
|
||||
_(MixPolicy<BoxPolicy<0>, BoxPolicy<1>, BoxPolicy<2>>) \
|
||||
_(MixPolicy<ObjectPolicy<0>, BoxPolicy<2>, ObjectPolicy<3>>) \
|
||||
_(MixPolicy<BoxExceptPolicy<0, MIRType::Object>, ObjectPolicy<1>>) \
|
||||
_(MixPolicy<UnboxedInt32Policy<0>, BigIntPolicy<1>>) \
|
||||
|
|
|
@ -112,6 +112,20 @@ class MOZ_STACK_CLASS CallInfo {
|
|||
setCallee(callee);
|
||||
setThis(thisVal);
|
||||
}
|
||||
|
||||
void initForProxyGet(MDefinition* callee, MDefinition* handler,
|
||||
MDefinition* target, MDefinition* id,
|
||||
MDefinition* receiver) {
|
||||
MOZ_ASSERT(args_.empty());
|
||||
setCallee(callee);
|
||||
setThis(handler);
|
||||
static_assert(decltype(args_)::InlineLength >= 3,
|
||||
"Appending three arguments should be infallible");
|
||||
MOZ_ALWAYS_TRUE(args_.append(target));
|
||||
MOZ_ALWAYS_TRUE(args_.append(id));
|
||||
MOZ_ALWAYS_TRUE(args_.append(receiver));
|
||||
}
|
||||
|
||||
void initForSetterCall(MDefinition* callee, MDefinition* thisVal,
|
||||
MDefinition* rhs) {
|
||||
MOZ_ASSERT(args_.empty());
|
||||
|
|
|
@ -295,6 +295,12 @@ class MOZ_RAII WarpCacheIRTranspiler : public WarpBuilderShared {
|
|||
bool sameRealm,
|
||||
uint32_t nargsAndFlagsOffset);
|
||||
|
||||
#ifndef JS_CODEGEN_X86
|
||||
[[nodiscard]] bool emitCallScriptedProxyGetShared(
|
||||
MDefinition* target, MDefinition* receiver, MDefinition* handler,
|
||||
MDefinition* id, MDefinition* trapDef, WrappedFunction* trap);
|
||||
#endif
|
||||
|
||||
CACHE_IR_TRANSPILER_GENERATED
|
||||
|
||||
public:
|
||||
|
@ -791,6 +797,18 @@ bool WarpCacheIRTranspiler::emitGuardIsTypedArray(ObjOperandId objId) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitGuardHasProxyHandler(ObjOperandId objId,
|
||||
uint32_t handlerOffset) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
const void* handler = rawPointerField(handlerOffset);
|
||||
|
||||
auto* ins = MGuardHasProxyHandler::New(alloc(), obj, handler);
|
||||
add(ins);
|
||||
|
||||
setOperand(objId, ins);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitGuardProto(ObjOperandId objId,
|
||||
uint32_t protoOffset) {
|
||||
MDefinition* def = getOperand(objId);
|
||||
|
@ -894,6 +912,26 @@ bool WarpCacheIRTranspiler::emitGuardDynamicSlotValue(ObjOperandId objId,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitLoadScriptedProxyHandler(ObjOperandId resultId,
|
||||
ObjOperandId objId) {
|
||||
MDefinition* obj = getOperand(objId);
|
||||
|
||||
auto* load = MLoadScriptedProxyHandler::New(alloc(), obj);
|
||||
add(load);
|
||||
|
||||
return defineOperand(resultId, load);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitIdToStringOrSymbol(ValOperandId resultId,
|
||||
ValOperandId idId) {
|
||||
MDefinition* id = getOperand(idId);
|
||||
|
||||
auto* ins = MIdToStringOrSymbol::New(alloc(), id);
|
||||
add(ins);
|
||||
|
||||
return defineOperand(resultId, ins);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitGuardSpecificAtom(StringOperandId strId,
|
||||
uint32_t expectedOffset) {
|
||||
MDefinition* str = getOperand(strId);
|
||||
|
@ -5104,6 +5142,80 @@ bool WarpCacheIRTranspiler::emitCallInlinedFunction(ObjOperandId calleeId,
|
|||
CallKind::Scripted);
|
||||
}
|
||||
|
||||
#ifdef JS_PUNBOX64
|
||||
bool WarpCacheIRTranspiler::emitCallScriptedProxyGetShared(
|
||||
MDefinition* target, MDefinition* receiver, MDefinition* handler,
|
||||
MDefinition* id, MDefinition* trapDef, WrappedFunction* trap) {
|
||||
CallInfo callInfo(alloc(), /* constructing = */ false,
|
||||
/* ignoresRval = */ false);
|
||||
callInfo.initForProxyGet(trapDef, handler, target, id, receiver);
|
||||
|
||||
MCall* call = makeCall(callInfo, /* needsThisCheck = */ false, trap);
|
||||
if (!call) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addEffectful(call);
|
||||
|
||||
if (!current->ensureHasSlots(3)) {
|
||||
return false;
|
||||
}
|
||||
current->push(call);
|
||||
current->push(id);
|
||||
current->push(target);
|
||||
|
||||
MResumePoint* resumePoint =
|
||||
MResumePoint::New(alloc(), current, loc_.toRawBytecode(),
|
||||
ResumeMode::ResumeAfterCheckProxyGetResult);
|
||||
if (!resumePoint) {
|
||||
return false;
|
||||
}
|
||||
call->setResumePoint(resumePoint);
|
||||
|
||||
current->pop();
|
||||
current->pop();
|
||||
|
||||
MCheckScriptedProxyGetResult* check =
|
||||
MCheckScriptedProxyGetResult::New(alloc(), target, id, call);
|
||||
add(check);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitCallScriptedProxyGetResult(
|
||||
ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
|
||||
uint32_t trapOffset, uint32_t idOffset, uint32_t nargsAndFlags) {
|
||||
MDefinition* target = getOperand(targetId);
|
||||
MDefinition* receiver = getOperand(receiverId);
|
||||
MDefinition* handler = getOperand(handlerId);
|
||||
MDefinition* trap = objectStubField(trapOffset);
|
||||
jsid id = idStubField(idOffset);
|
||||
MDefinition* idDef = constant(StringValue(id.toAtom()));
|
||||
uint16_t nargs = nargsAndFlags >> 16;
|
||||
FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
|
||||
WrappedFunction* wrappedTarget =
|
||||
maybeWrappedFunction(trap, CallKind::Scripted, nargs, flags);
|
||||
return emitCallScriptedProxyGetShared(target, receiver, handler, idDef, trap,
|
||||
wrappedTarget);
|
||||
}
|
||||
|
||||
bool WarpCacheIRTranspiler::emitCallScriptedProxyGetByValueResult(
|
||||
ValOperandId targetId, ObjOperandId receiverId, ObjOperandId handlerId,
|
||||
ValOperandId idId, uint32_t trapOffset, uint32_t nargsAndFlags) {
|
||||
MDefinition* target = getOperand(targetId);
|
||||
MDefinition* receiver = getOperand(receiverId);
|
||||
MDefinition* handler = getOperand(handlerId);
|
||||
MDefinition* trap = objectStubField(trapOffset);
|
||||
MDefinition* idDef = getOperand(idId);
|
||||
uint16_t nargs = nargsAndFlags >> 16;
|
||||
FunctionFlags flags = FunctionFlags(uint16_t(nargsAndFlags));
|
||||
WrappedFunction* wrappedTarget =
|
||||
maybeWrappedFunction(trap, CallKind::Scripted, nargs, flags);
|
||||
return emitCallScriptedProxyGetShared(target, receiver, handler, idDef, trap,
|
||||
wrappedTarget);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool WarpCacheIRTranspiler::emitCallClassHook(ObjOperandId calleeId,
|
||||
Int32OperandId argcId,
|
||||
CallFlags flags,
|
||||
|
|
|
@ -1239,12 +1239,7 @@ static PropertyFlags ComputePropertyFlags(const PropertyDescriptor& desc) {
|
|||
flags.setFlag(PropertyFlag::Writable, desc.writable());
|
||||
} else {
|
||||
MOZ_ASSERT(desc.isAccessorDescriptor());
|
||||
if (desc.hasGetter()) {
|
||||
flags.setFlag(PropertyFlag::HasGetter);
|
||||
}
|
||||
if (desc.hasSetter()) {
|
||||
flags.setFlag(PropertyFlag::HasSetter);
|
||||
}
|
||||
flags.setFlag(PropertyFlag::AccessorProperty);
|
||||
}
|
||||
|
||||
return flags;
|
||||
|
|
|
@ -42,10 +42,13 @@ GetObjectFlagsForNewProperty(const JSClass* clasp, ObjectFlags flags, jsid id,
|
|||
// JIT.
|
||||
if (propFlags.isDataProperty() && !propFlags.writable()) {
|
||||
flags.setFlag(ObjectFlag::NeedsProxyGetSetResultValidation);
|
||||
} else if (propFlags.hasGetter() != propFlags.hasSetter()) {
|
||||
} else if (propFlags.isAccessorProperty()) {
|
||||
// This will cover us for both get trap validation and set trap
|
||||
// validation. We could be more aggressive and have one flag for each,
|
||||
// since their requirements are inverted, but this should be fine.
|
||||
// validation. We could be more aggressive, because what we really
|
||||
// care about is if there is a getter but not a setter and vice
|
||||
// versa, but the first pass at doing that resulted in test
|
||||
// failures. We'll need to work on that as a follow-up if it is
|
||||
// important.
|
||||
flags.setFlag(ObjectFlag::NeedsProxyGetSetResultValidation);
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +74,9 @@ inline ObjectFlags CopyPropMapObjectFlags(ObjectFlags dest,
|
|||
if (source.hasFlag(ObjectFlag::HasNonWritableOrAccessorPropExclProto)) {
|
||||
dest.setFlag(ObjectFlag::HasNonWritableOrAccessorPropExclProto);
|
||||
}
|
||||
if (source.hasFlag(ObjectFlag::NeedsProxyGetSetResultValidation)) {
|
||||
dest.setFlag(ObjectFlag::NeedsProxyGetSetResultValidation);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
|
|
@ -1094,8 +1094,7 @@ void PropMap::dump(js::GenericPrinter& out) const {
|
|||
dumpFlag(PropertyFlag::Enumerable, "enumerable");
|
||||
dumpFlag(PropertyFlag::Configurable, "configurable");
|
||||
dumpFlag(PropertyFlag::Writable, "writable");
|
||||
dumpFlag(PropertyFlag::HasGetter, "hasGetter");
|
||||
dumpFlag(PropertyFlag::HasSetter, "hasSetter");
|
||||
dumpFlag(PropertyFlag::AccessorProperty, "accessor");
|
||||
dumpFlag(PropertyFlag::CustomDataProperty, "custom-data");
|
||||
out.putChar(')');
|
||||
}
|
||||
|
|
|
@ -32,11 +32,9 @@ enum class PropertyFlag : uint8_t {
|
|||
Enumerable = 1 << 1,
|
||||
Writable = 1 << 2,
|
||||
|
||||
// Whether this property is an accessor property and has a getter
|
||||
HasGetter = 1 << 3,
|
||||
|
||||
// Whether this property is an accessor property and has a setter
|
||||
HasSetter = 1 << 4,
|
||||
// Whether this is an accessor property. Accessor properties have a slot that
|
||||
// stores a GetterSetter instance.
|
||||
AccessorProperty = 1 << 3,
|
||||
|
||||
// If set, this is a custom data property. The property is exposed as a data
|
||||
// property to JS code and PropertyDescriptor, but instead of an object slot
|
||||
|
@ -46,7 +44,7 @@ enum class PropertyFlag : uint8_t {
|
|||
// properties.
|
||||
//
|
||||
// This flag is deprecated (we don't want to add more uses).
|
||||
CustomDataProperty = 1 << 5,
|
||||
CustomDataProperty = 1 << 4,
|
||||
};
|
||||
|
||||
class PropertyFlags : public EnumFlags<PropertyFlag> {
|
||||
|
@ -70,9 +68,9 @@ class PropertyFlags : public EnumFlags<PropertyFlag> {
|
|||
bool isDataProperty() const {
|
||||
return !isAccessorProperty() && !isCustomDataProperty();
|
||||
}
|
||||
bool isAccessorProperty() const { return hasGetter() || hasSetter(); }
|
||||
bool hasGetter() const { return hasFlag(PropertyFlag::HasGetter); }
|
||||
bool hasSetter() const { return hasFlag(PropertyFlag::HasSetter); }
|
||||
bool isAccessorProperty() const {
|
||||
return hasFlag(PropertyFlag::AccessorProperty);
|
||||
}
|
||||
bool isCustomDataProperty() const {
|
||||
return hasFlag(PropertyFlag::CustomDataProperty);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче