[ic] Port {Load,Store}IC_Normal to TF
BUG=v8:5269 Review-Url: https://codereview.chromium.org/2622003004 Cr-Commit-Position: refs/heads/master@{#42261}
This commit is contained in:
parent
3d9e2ea32d
commit
d23e7d2f81
@ -2650,84 +2650,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ b(ne, miss);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found. Jump to
|
||||
// the |miss| label otherwise.
|
||||
// If lookup was successful |scratch2| will be equal to elements + 4 * index.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!elements.is(scratch1));
|
||||
DCHECK(!elements.is(scratch2));
|
||||
DCHECK(!name.is(scratch1));
|
||||
DCHECK(!name.is(scratch2));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ ldr(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ SmiUntag(scratch1);
|
||||
__ sub(scratch1, scratch1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ ldr(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ add(scratch2, scratch2, Operand(
|
||||
NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ and_(scratch2, scratch1, Operand(scratch2, LSR, Name::kHashShift));
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
// scratch2 = scratch2 * 3.
|
||||
__ add(scratch2, scratch2, Operand(scratch2, LSL, 1));
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ add(scratch2, elements, Operand(scratch2, LSL, 2));
|
||||
__ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ cmp(name, Operand(ip));
|
||||
__ b(eq, done);
|
||||
}
|
||||
|
||||
const int spill_mask =
|
||||
(lr.bit() | r6.bit() | r5.bit() | r4.bit() |
|
||||
r3.bit() | r2.bit() | r1.bit() | r0.bit()) &
|
||||
~(scratch1.bit() | scratch2.bit());
|
||||
|
||||
__ stm(db_w, sp, spill_mask);
|
||||
if (name.is(r0)) {
|
||||
DCHECK(!elements.is(r1));
|
||||
__ Move(r1, name);
|
||||
__ Move(r0, elements);
|
||||
} else {
|
||||
__ Move(r0, elements);
|
||||
__ Move(r1, name);
|
||||
}
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ cmp(r0, Operand::Zero());
|
||||
__ mov(scratch2, Operand(r2));
|
||||
__ ldm(ia_w, sp, spill_mask);
|
||||
|
||||
__ b(ne, done);
|
||||
__ b(eq, miss);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -269,14 +269,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -3081,91 +3081,6 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
|
||||
__ Blr(lr);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the 'elements' register.
|
||||
// Jump to the 'done' label if a property with the given name is found.
|
||||
// Jump to the 'miss' label otherwise.
|
||||
//
|
||||
// If lookup was successful 'scratch2' will be equal to elements + 4 * index.
|
||||
// 'elements' and 'name' registers are preserved on miss.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(
|
||||
MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!AreAliased(elements, name, scratch1, scratch2));
|
||||
|
||||
// Assert that name contains a string.
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ Ldrsw(scratch1, UntagSmiFieldMemOperand(elements, kCapacityOffset));
|
||||
__ Sub(scratch1, scratch1, 1);
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before giving up.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ Ldr(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ Add(scratch2, scratch2, Operand(
|
||||
NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ And(scratch2, scratch1, Operand(scratch2, LSR, Name::kHashShift));
|
||||
|
||||
// Scale the index by multiplying by the element size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
__ Add(scratch2, scratch2, Operand(scratch2, LSL, 1));
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
UseScratchRegisterScope temps(masm);
|
||||
Register scratch3 = temps.AcquireX();
|
||||
__ Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2));
|
||||
__ Ldr(scratch3, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ Cmp(name, scratch3);
|
||||
__ B(eq, done);
|
||||
}
|
||||
|
||||
// The inlined probes didn't find the entry.
|
||||
// Call the complete stub to scan the whole dictionary.
|
||||
|
||||
CPURegList spill_list(CPURegister::kRegister, kXRegSizeInBits, 0, 6);
|
||||
spill_list.Combine(lr);
|
||||
spill_list.Remove(scratch1);
|
||||
spill_list.Remove(scratch2);
|
||||
|
||||
__ PushCPURegList(spill_list);
|
||||
|
||||
if (name.is(x0)) {
|
||||
DCHECK(!elements.is(x1));
|
||||
__ Mov(x1, name);
|
||||
__ Mov(x0, elements);
|
||||
} else {
|
||||
__ Mov(x0, elements);
|
||||
__ Mov(x1, name);
|
||||
}
|
||||
|
||||
Label not_found;
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ Cbz(x0, ¬_found);
|
||||
__ Mov(scratch2, x2); // Move entry index into scratch2.
|
||||
__ PopCPURegList(spill_list);
|
||||
__ B(done);
|
||||
|
||||
__ Bind(¬_found);
|
||||
__ PopCPURegList(spill_list);
|
||||
__ B(miss);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
|
@ -355,14 +355,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register scratch1,
|
||||
Register scratch2);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -108,8 +108,34 @@ void Builtins::Generate_LoadIC_Miss(compiler::CodeAssemblerState* state) {
|
||||
slot, vector);
|
||||
}
|
||||
|
||||
void Builtins::Generate_LoadIC_Normal(MacroAssembler* masm) {
|
||||
LoadIC::GenerateNormal(masm);
|
||||
TF_BUILTIN(LoadIC_Normal, CodeStubAssembler) {
|
||||
typedef LoadWithVectorDescriptor Descriptor;
|
||||
|
||||
Node* receiver = Parameter(Descriptor::kReceiver);
|
||||
Node* name = Parameter(Descriptor::kName);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label slow(this);
|
||||
{
|
||||
Node* properties = LoadProperties(receiver);
|
||||
Variable var_name_index(this, MachineType::PointerRepresentation());
|
||||
Label found(this, &var_name_index);
|
||||
NameDictionaryLookup<NameDictionary>(properties, name, &found,
|
||||
&var_name_index, &slow);
|
||||
Bind(&found);
|
||||
{
|
||||
Variable var_details(this, MachineRepresentation::kWord32);
|
||||
Variable var_value(this, MachineRepresentation::kTagged);
|
||||
LoadPropertyFromNameDictionary(properties, var_name_index.value(),
|
||||
&var_details, &var_value);
|
||||
Node* value = CallGetterIfAccessor(var_value.value(), var_details.value(),
|
||||
context, receiver, &slow);
|
||||
Return(value);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&slow);
|
||||
TailCallRuntime(Runtime::kGetProperty, context, receiver, name);
|
||||
}
|
||||
|
||||
void Builtins::Generate_LoadIC_Slow(compiler::CodeAssemblerState* state) {
|
||||
@ -140,8 +166,47 @@ void Builtins::Generate_StoreIC_Miss(compiler::CodeAssemblerState* state) {
|
||||
vector, receiver, name);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StoreIC_Normal(MacroAssembler* masm) {
|
||||
StoreIC::GenerateNormal(masm);
|
||||
TF_BUILTIN(StoreIC_Normal, CodeStubAssembler) {
|
||||
typedef StoreWithVectorDescriptor Descriptor;
|
||||
|
||||
Node* receiver = Parameter(Descriptor::kReceiver);
|
||||
Node* name = Parameter(Descriptor::kName);
|
||||
Node* value = Parameter(Descriptor::kValue);
|
||||
Node* slot = Parameter(Descriptor::kSlot);
|
||||
Node* vector = Parameter(Descriptor::kVector);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Label slow(this);
|
||||
{
|
||||
Node* properties = LoadProperties(receiver);
|
||||
Variable var_name_index(this, MachineType::PointerRepresentation());
|
||||
Label found(this, &var_name_index);
|
||||
NameDictionaryLookup<NameDictionary>(properties, name, &found,
|
||||
&var_name_index, &slow);
|
||||
Bind(&found);
|
||||
{
|
||||
const int kNameToDetailsOffset = (NameDictionary::kEntryDetailsIndex -
|
||||
NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
Node* details = LoadFixedArrayElement(properties, var_name_index.value(),
|
||||
kNameToDetailsOffset);
|
||||
// Check that the property is a writable data property (no accessor).
|
||||
const int kTypeAndReadOnlyMask = PropertyDetails::KindField::kMask |
|
||||
PropertyDetails::kAttributesReadOnlyMask;
|
||||
STATIC_ASSERT(kData == 0);
|
||||
GotoIf(IsSetSmi(details, kTypeAndReadOnlyMask), &slow);
|
||||
const int kNameToValueOffset =
|
||||
(NameDictionary::kEntryValueIndex - NameDictionary::kEntryKeyIndex) *
|
||||
kPointerSize;
|
||||
StoreFixedArrayElement(properties, var_name_index.value(), value,
|
||||
UPDATE_WRITE_BARRIER, kNameToValueOffset);
|
||||
Return(value);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&slow);
|
||||
TailCallRuntime(Runtime::kStoreIC_Miss, context, value, slot, vector,
|
||||
receiver, name);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StoreIC_Setter_ForDeopt(MacroAssembler* masm) {
|
||||
|
@ -224,10 +224,10 @@ namespace internal {
|
||||
TFS(LoadGlobalIC_Slow, HANDLER, Code::LOAD_GLOBAL_IC, LoadGlobalWithVector) \
|
||||
ASH(LoadIC_Getter_ForDeopt, LOAD_IC, kNoExtraICState) \
|
||||
TFS(LoadIC_Miss, BUILTIN, kNoExtraICState, LoadWithVector) \
|
||||
ASH(LoadIC_Normal, HANDLER, Code::LOAD_IC) \
|
||||
TFS(LoadIC_Normal, HANDLER, Code::LOAD_IC, LoadWithVector) \
|
||||
TFS(LoadIC_Slow, HANDLER, Code::LOAD_IC, LoadWithVector) \
|
||||
TFS(StoreIC_Miss, BUILTIN, kNoExtraICState, StoreWithVector) \
|
||||
ASH(StoreIC_Normal, HANDLER, Code::STORE_IC) \
|
||||
TFS(StoreIC_Normal, HANDLER, Code::STORE_IC, StoreWithVector) \
|
||||
ASH(StoreIC_Setter_ForDeopt, STORE_IC, StoreICState::kStrictModeState) \
|
||||
TFS(StoreIC_SlowSloppy, HANDLER, Code::STORE_IC, StoreWithVector) \
|
||||
TFS(StoreIC_SlowStrict, HANDLER, Code::STORE_IC, StoreWithVector) \
|
||||
|
@ -2644,67 +2644,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found leaving the
|
||||
// index into the dictionary in |r0|. Jump to the |miss| label
|
||||
// otherwise.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1) {
|
||||
DCHECK(!elements.is(r0));
|
||||
DCHECK(!elements.is(r1));
|
||||
DCHECK(!name.is(r0));
|
||||
DCHECK(!name.is(r1));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
__ mov(r1, FieldOperand(elements, kCapacityOffset));
|
||||
__ shr(r1, kSmiTagSize); // convert smi to int
|
||||
__ dec(r1);
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shr(r0, Name::kHashShift);
|
||||
if (i > 0) {
|
||||
__ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ and_(r0, r1);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
__ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ cmp(name, Operand(elements,
|
||||
r0,
|
||||
times_4,
|
||||
kElementsStartOffset - kHeapObjectTag));
|
||||
__ j(equal, done);
|
||||
}
|
||||
|
||||
NameDictionaryLookupStub stub(masm->isolate(), elements, r1, r0,
|
||||
POSITIVE_LOOKUP);
|
||||
__ push(name);
|
||||
__ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shr(r0, Name::kHashShift);
|
||||
__ push(r0);
|
||||
__ CallStub(&stub);
|
||||
|
||||
__ test(r1, r1);
|
||||
__ j(zero, miss);
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -58,14 +58,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register r0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -19,133 +19,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ tst(scratch1, Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
|
||||
__ b(ne, miss);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ ldr(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
(PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY))
|
||||
<< kSmiTagSize;
|
||||
__ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ tst(scratch1, Operand(kTypeAndReadOnlyMask));
|
||||
__ b(ne, miss);
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ str(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ mov(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = r0;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), r0, r3, r4);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return r3; }
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
|
||||
__ mov(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -169,39 +42,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = r5;
|
||||
DCHECK(receiver.is(r1));
|
||||
DCHECK(name.is(r2));
|
||||
DCHECK(value.is(r0));
|
||||
DCHECK(StoreWithVectorDescriptor::VectorRegister().is(r3));
|
||||
DCHECK(StoreWithVectorDescriptor::SlotRegister().is(r4));
|
||||
|
||||
__ ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, r6, r9);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, r6, r9);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, r6, r9);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -15,120 +15,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done.
|
||||
// The scratch registers need to be different from elements, name and result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!AreAliased(elements, name, scratch1, scratch2));
|
||||
DCHECK(!AreAliased(result, scratch1, scratch2));
|
||||
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal property.
|
||||
__ Bind(&done);
|
||||
|
||||
static const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ Ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ Tst(scratch1, Smi::FromInt(PropertyDetails::TypeField::kMask));
|
||||
__ B(ne, miss);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ Ldr(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store (never clobbered).
|
||||
//
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!AreAliased(elements, name, value, scratch1, scratch2));
|
||||
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ Bind(&done);
|
||||
|
||||
static const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
static const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
static const int kTypeAndReadOnlyMask =
|
||||
PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY);
|
||||
__ Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ Tst(scratch1, kTypeAndReadOnlyMask);
|
||||
__ B(ne, miss);
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
static const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ Add(scratch2, scratch2, kValueOffset - kHeapObjectTag);
|
||||
__ Str(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ Mov(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = x0;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
Label slow;
|
||||
|
||||
__ Ldr(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), x0, x3, x4);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ Bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
__ Push(LoadDescriptor::ReceiverRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -153,38 +39,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register dictionary = x5;
|
||||
DCHECK(!AreAliased(value, receiver, name,
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
StoreWithVectorDescriptor::VectorRegister(), x5, x6, x7));
|
||||
|
||||
__ Ldr(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, x6, x7);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, x6, x7);
|
||||
__ Ret();
|
||||
|
||||
// Cache miss: Jump to runtime.
|
||||
__ Bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, x6, x7);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
Condition CompareIC::ComputeCondition(Token::Value op) {
|
||||
switch (op) {
|
||||
case Token::EQ_STRICT:
|
||||
|
@ -18,141 +18,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used to load a property from a dictionary backing
|
||||
// storage. This function may fail to load a property even though it is
|
||||
// in the dictionary, so code at miss_label must always call a backup
|
||||
// property load that is complete. This function is safe to call if
|
||||
// name is not internalized, and will jump to the miss_label in that
|
||||
// case. The generated code assumes that the receiver has slow
|
||||
// properties, is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register r0, Register r1, Register result) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is unchanged.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// Scratch registers:
|
||||
//
|
||||
// r0 - used for the index into the property dictionary
|
||||
//
|
||||
// r1 - used to hold the capacity of the property dictionary.
|
||||
//
|
||||
// result - holds the result on exit.
|
||||
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
|
||||
elements, name, r0, r1);
|
||||
|
||||
// If probing finds an entry in the dictionary, r0 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used to store a property to a dictionary backing
|
||||
// storage. This function may fail to store a property eventhough it
|
||||
// is in the dictionary, so code at miss_label must always call a
|
||||
// backup property store that is complete. This function is safe to
|
||||
// call if name is not internalized, and will jump to the miss_label in
|
||||
// that case. The generated code assumes that the receiver has slow
|
||||
// properties, is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register value, Register r0, Register r1) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is clobbered.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// value - holds the value to store and is unchanged.
|
||||
//
|
||||
// r0 - used for index into the property dictionary and is clobbered.
|
||||
//
|
||||
// r1 - used to hold the capacity of the property dictionary and is clobbered.
|
||||
Label done;
|
||||
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
|
||||
elements, name, r0, r1);
|
||||
|
||||
// If probing finds an entry in the dictionary, r0 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property that is not read only.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
(PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY))
|
||||
<< kSmiTagSize;
|
||||
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
Immediate(kTypeAndReadOnlyMask));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Store the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
|
||||
__ mov(Operand(r0, 0), value);
|
||||
|
||||
// Update write barrier. Make sure not to clobber the value.
|
||||
__ mov(r1, value);
|
||||
__ RecordWrite(elements, r0, r1, kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = eax;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ mov(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), edi, ebx, eax);
|
||||
__ ret(0);
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register name = LoadDescriptor::NameRegister();
|
||||
DCHECK(!ebx.is(receiver) && !ebx.is(name));
|
||||
|
||||
__ pop(ebx);
|
||||
__ push(receiver);
|
||||
__ push(name);
|
||||
__ push(ebx);
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
Register receiver = StoreWithVectorDescriptor::ReceiverRegister();
|
||||
Register name = StoreWithVectorDescriptor::NameRegister();
|
||||
@ -171,50 +36,6 @@ static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ push(return_address);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
typedef StoreWithVectorDescriptor Descriptor;
|
||||
Label restore_miss;
|
||||
Register receiver = Descriptor::ReceiverRegister();
|
||||
Register name = Descriptor::NameRegister();
|
||||
Register value = Descriptor::ValueRegister();
|
||||
// Since the slot and vector values are passed on the stack we can use
|
||||
// respective registers as scratch registers.
|
||||
Register scratch1 = Descriptor::VectorRegister();
|
||||
Register scratch2 = Descriptor::SlotRegister();
|
||||
|
||||
__ LoadParameterFromStack<Descriptor>(value, Descriptor::kValue);
|
||||
|
||||
// A lot of registers are needed for storing to slow case objects.
|
||||
// Push and restore receiver but rely on GenerateDictionaryStore preserving
|
||||
// the value and name.
|
||||
__ push(receiver);
|
||||
|
||||
Register dictionary = receiver;
|
||||
__ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryStore(masm, &restore_miss, dictionary, name, value,
|
||||
scratch1, scratch2);
|
||||
__ Drop(1);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1);
|
||||
__ ret(Descriptor::kStackArgumentsCount * kPointerSize);
|
||||
|
||||
__ bind(&restore_miss);
|
||||
__ pop(receiver);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
@ -285,10 +285,6 @@ class LoadIC : public IC {
|
||||
NOT_INSIDE_TYPEOF;
|
||||
}
|
||||
|
||||
// Code generator routines.
|
||||
static void GenerateRuntimeGetProperty(MacroAssembler* masm);
|
||||
static void GenerateNormal(MacroAssembler* masm);
|
||||
|
||||
MUST_USE_RESULT MaybeHandle<Object> Load(Handle<Object> object,
|
||||
Handle<Name> name);
|
||||
|
||||
@ -374,11 +370,6 @@ class StoreIC : public IC {
|
||||
return StoreICState::GetLanguageMode(extra_ic_state());
|
||||
}
|
||||
|
||||
// Code generators for stub routines. Only called once at startup.
|
||||
static void GenerateSlow(MacroAssembler* masm);
|
||||
static void GenerateMiss(MacroAssembler* masm);
|
||||
static void GenerateNormal(MacroAssembler* masm);
|
||||
|
||||
MUST_USE_RESULT MaybeHandle<Object> Store(
|
||||
Handle<Object> object, Handle<Name> name, Handle<Object> value,
|
||||
JSReceiver::StoreFromKeyed store_mode =
|
||||
|
@ -19,138 +19,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
// The address returned from GenerateStringDictionaryProbes() in scratch2
|
||||
// is used.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index.
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ And(at, scratch1,
|
||||
Operand(PropertyDetails::TypeField::kMask << kSmiTagSize));
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ lw(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
// The address returned from GenerateStringDictionaryProbes() in scratch2
|
||||
// is used.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index.
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
(PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY))
|
||||
<< kSmiTagSize;
|
||||
__ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ And(at, scratch1, Operand(kTypeAndReadOnlyMask));
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ Addu(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ sw(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ mov(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = a0;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ lw(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), v0, a3, t0);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return a3; }
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in ra.
|
||||
|
||||
__ mov(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -174,40 +42,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = t1;
|
||||
DCHECK(receiver.is(a1));
|
||||
DCHECK(name.is(a2));
|
||||
DCHECK(value.is(a0));
|
||||
DCHECK(StoreWithVectorDescriptor::VectorRegister().is(a3));
|
||||
DCHECK(StoreWithVectorDescriptor::SlotRegister().is(t0));
|
||||
|
||||
__ lw(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, t2, t5);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, t2, t5);
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ Move(v0, value); // Ensure the stub returns correct value.
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, t2, t5);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -19,136 +19,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
// The address returned from GenerateStringDictionaryProbes() in scratch2
|
||||
// is used.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index.
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ ld(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ And(at, scratch1,
|
||||
Operand(Smi::FromInt(PropertyDetails::TypeField::kMask)));
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ ld(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
// The address returned from GenerateStringDictionaryProbes() in scratch2
|
||||
// is used.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index.
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
(PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY));
|
||||
__ ld(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ And(at, scratch1, Operand(Smi::FromInt(kTypeAndReadOnlyMask)));
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ Daddu(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ sd(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ mov(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = a0;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
Label slow;
|
||||
|
||||
__ ld(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), v0, a3, a4);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return a3; }
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in ra.
|
||||
|
||||
__ mov(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -172,38 +42,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = a5;
|
||||
DCHECK(!AreAliased(
|
||||
value, receiver, name, StoreWithVectorDescriptor::VectorRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(), dictionary, a6, a7));
|
||||
|
||||
__ ld(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, a6, a7);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, a6, a7);
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ Move(v0, value); // Ensure the stub returns correct value.
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, a6, a7);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -19,138 +19,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ mr(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(PropertyDetails::TypeField::kMask));
|
||||
__ and_(scratch2, scratch1, scratch2, SetRC);
|
||||
__ bne(miss, cr0);
|
||||
__ mr(scratch2, r0);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ LoadP(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
int kTypeAndReadOnlyMask =
|
||||
PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY);
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ mr(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(kTypeAndReadOnlyMask));
|
||||
__ and_(scratch2, scratch1, scratch2, SetRC);
|
||||
__ bne(miss, cr0);
|
||||
__ mr(scratch2, r0);
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ addi(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ StoreP(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ mr(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = r3;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), r3, r6, r7);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return r6; }
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
|
||||
__ mr(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -174,39 +42,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = r8;
|
||||
DCHECK(receiver.is(r4));
|
||||
DCHECK(name.is(r5));
|
||||
DCHECK(value.is(r3));
|
||||
DCHECK(StoreWithVectorDescriptor::VectorRegister().is(r6));
|
||||
DCHECK(StoreWithVectorDescriptor::SlotRegister().is(r7));
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, r9, r10);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, r9, r10);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, r9, r10);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
|
||||
|
@ -18,136 +18,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used from LoadIC GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// result: Register for the result. It is only updated if a jump to the miss
|
||||
// label is not done. Can be the same as elements or name clobbering
|
||||
// one of these in the case of not jumping to the miss label.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register result, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ LoadRR(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(PropertyDetails::TypeField::kMask));
|
||||
__ AndP(scratch2, scratch1);
|
||||
__ bne(miss);
|
||||
__ LoadRR(scratch2, r0);
|
||||
|
||||
// Get the value at the masked, scaled index and return.
|
||||
__ LoadP(result,
|
||||
FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
|
||||
}
|
||||
|
||||
// Helper function used from StoreIC::GenerateNormal.
|
||||
//
|
||||
// elements: Property dictionary. It is not clobbered if a jump to the miss
|
||||
// label is done.
|
||||
// name: Property name. It is not clobbered if a jump to the miss label is
|
||||
// done
|
||||
// value: The value to store.
|
||||
// The two scratch registers need to be different from elements, name and
|
||||
// result.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch1,
|
||||
Register scratch2) {
|
||||
// Main use of the scratch registers.
|
||||
// scratch1: Used as temporary and to hold the capacity of the property
|
||||
// dictionary.
|
||||
// scratch2: Used as temporary.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss, &done, elements,
|
||||
name, scratch1, scratch2);
|
||||
|
||||
// If probing finds an entry in the dictionary check that the value
|
||||
// is a normal property that is not read only.
|
||||
__ bind(&done); // scratch2 == elements + 4 * index
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
int kTypeAndReadOnlyMask =
|
||||
PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY);
|
||||
__ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
||||
__ LoadRR(r0, scratch2);
|
||||
__ LoadSmiLiteral(scratch2, Smi::FromInt(kTypeAndReadOnlyMask));
|
||||
__ AndP(scratch2, scratch1);
|
||||
__ bne(miss /*, cr0*/);
|
||||
__ LoadRR(scratch2, r0);
|
||||
|
||||
// Store the value at the masked, scaled index and return.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ AddP(scratch2, Operand(kValueOffset - kHeapObjectTag));
|
||||
__ StoreP(value, MemOperand(scratch2));
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ LoadRR(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1, kLRHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = r2;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), r2, r5, r6);
|
||||
__ Ret();
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
// A register that isn't one of the parameters to the load ic.
|
||||
static const Register LoadIC_TempRegister() { return r5; }
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is in lr.
|
||||
|
||||
__ LoadRR(LoadIC_TempRegister(), LoadDescriptor::ReceiverRegister());
|
||||
__ Push(LoadIC_TempRegister(), LoadDescriptor::NameRegister());
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ Push(StoreWithVectorDescriptor::ValueRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister(),
|
||||
@ -170,37 +40,6 @@ void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
|
||||
__ TailCallRuntime(Runtime::kKeyedStoreIC_Slow);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = r7;
|
||||
DCHECK(receiver.is(r3));
|
||||
DCHECK(name.is(r4));
|
||||
DCHECK(value.is(r2));
|
||||
DCHECK(StoreWithVectorDescriptor::VectorRegister().is(r5));
|
||||
DCHECK(StoreWithVectorDescriptor::SlotRegister().is(r6));
|
||||
|
||||
__ LoadP(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
||||
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, r8, r9);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1, r8, r9);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1, r8, r9);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
#undef __
|
||||
|
||||
Condition CompareIC::ComputeCondition(Token::Value op) {
|
||||
|
@ -18,143 +18,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used to load a property from a dictionary backing storage.
|
||||
// This function may return false negatives, so miss_label
|
||||
// must always call a backup property load that is complete.
|
||||
// This function is safe to call if name is not an internalized string,
|
||||
// and will jump to the miss_label in that case.
|
||||
// The generated code assumes that the receiver has slow properties,
|
||||
// is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register r0, Register r1, Register result) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is unchanged.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// r0 - used to hold the capacity of the property dictionary.
|
||||
//
|
||||
// r1 - used to hold the index into the property dictionary.
|
||||
//
|
||||
// result - holds the result on exit if the load succeeded.
|
||||
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
|
||||
elements, name, r0, r1);
|
||||
|
||||
// If probing finds an entry in the dictionary, r1 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ Test(Operand(elements, r1, times_pointer_size,
|
||||
kDetailsOffset - kHeapObjectTag),
|
||||
Smi::FromInt(PropertyDetails::TypeField::kMask));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ movp(result, Operand(elements, r1, times_pointer_size,
|
||||
kValueOffset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used to store a property to a dictionary backing
|
||||
// storage. This function may fail to store a property even though it
|
||||
// is in the dictionary, so code at miss_label must always call a
|
||||
// backup property store that is complete. This function is safe to
|
||||
// call if name is not an internalized string, and will jump to the miss_label
|
||||
// in that case. The generated code assumes that the receiver has slow
|
||||
// properties, is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register value, Register scratch0,
|
||||
Register scratch1) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is clobbered.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// value - holds the value to store and is unchanged.
|
||||
//
|
||||
// scratch0 - used during the positive dictionary lookup and is clobbered.
|
||||
//
|
||||
// scratch1 - used for index into the property dictionary and is clobbered.
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(
|
||||
masm, miss_label, &done, elements, name, scratch0, scratch1);
|
||||
|
||||
// If probing finds an entry in the dictionary, scratch0 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property that is not read only.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY);
|
||||
__ Test(Operand(elements, scratch1, times_pointer_size,
|
||||
kDetailsOffset - kHeapObjectTag),
|
||||
Smi::FromInt(kTypeAndReadOnlyMask));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Store the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ leap(scratch1, Operand(elements, scratch1, times_pointer_size,
|
||||
kValueOffset - kHeapObjectTag));
|
||||
__ movp(Operand(scratch1, 0), value);
|
||||
|
||||
// Update write barrier. Make sure not to clobber the value.
|
||||
__ movp(scratch0, value);
|
||||
__ RecordWrite(elements, scratch1, scratch0, kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = rax;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ movp(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), rbx, rdi, rax);
|
||||
__ ret(0);
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
LoadIC::GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// The return address is on the stack.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register name = LoadDescriptor::NameRegister();
|
||||
|
||||
DCHECK(!rbx.is(receiver) && !rbx.is(name));
|
||||
|
||||
__ PopReturnAddressTo(rbx);
|
||||
__ Push(receiver);
|
||||
__ Push(name);
|
||||
__ PushReturnAddressFrom(rbx);
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
Register receiver = StoreWithVectorDescriptor::ReceiverRegister();
|
||||
Register name = StoreWithVectorDescriptor::NameRegister();
|
||||
@ -173,38 +36,6 @@ static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ PushReturnAddressFrom(temp);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register receiver = StoreDescriptor::ReceiverRegister();
|
||||
Register name = StoreDescriptor::NameRegister();
|
||||
Register value = StoreDescriptor::ValueRegister();
|
||||
Register dictionary = r11;
|
||||
DCHECK(!AreAliased(dictionary, StoreWithVectorDescriptor::VectorRegister(),
|
||||
StoreWithVectorDescriptor::SlotRegister()));
|
||||
|
||||
Label miss;
|
||||
|
||||
__ movp(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryStore(masm, &miss, dictionary, name, value, r8, r9);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1);
|
||||
__ ret(0);
|
||||
|
||||
__ bind(&miss);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
@ -18,141 +18,6 @@ namespace internal {
|
||||
|
||||
#define __ ACCESS_MASM(masm)
|
||||
|
||||
// Helper function used to load a property from a dictionary backing
|
||||
// storage. This function may fail to load a property even though it is
|
||||
// in the dictionary, so code at miss_label must always call a backup
|
||||
// property load that is complete. This function is safe to call if
|
||||
// name is not internalized, and will jump to the miss_label in that
|
||||
// case. The generated code assumes that the receiver has slow
|
||||
// properties, is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register r0, Register r1, Register result) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is unchanged.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// Scratch registers:
|
||||
//
|
||||
// r0 - used for the index into the property dictionary
|
||||
//
|
||||
// r1 - used to hold the capacity of the property dictionary.
|
||||
//
|
||||
// result - holds the result on exit.
|
||||
|
||||
Label done;
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
|
||||
elements, name, r0, r1);
|
||||
|
||||
// If probing finds an entry in the dictionary, r0 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Get the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ mov(result, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
|
||||
}
|
||||
|
||||
|
||||
// Helper function used to store a property to a dictionary backing
|
||||
// storage. This function may fail to store a property eventhough it
|
||||
// is in the dictionary, so code at miss_label must always call a
|
||||
// backup property store that is complete. This function is safe to
|
||||
// call if name is not internalized, and will jump to the miss_label in
|
||||
// that case. The generated code assumes that the receiver has slow
|
||||
// properties, is not a global object and does not have interceptors.
|
||||
static void GenerateDictionaryStore(MacroAssembler* masm, Label* miss_label,
|
||||
Register elements, Register name,
|
||||
Register value, Register r0, Register r1) {
|
||||
// Register use:
|
||||
//
|
||||
// elements - holds the property dictionary on entry and is clobbered.
|
||||
//
|
||||
// name - holds the name of the property on entry and is unchanged.
|
||||
//
|
||||
// value - holds the value to store and is unchanged.
|
||||
//
|
||||
// r0 - used for index into the property dictionary and is clobbered.
|
||||
//
|
||||
// r1 - used to hold the capacity of the property dictionary and is clobbered.
|
||||
Label done;
|
||||
|
||||
|
||||
// Probe the dictionary.
|
||||
NameDictionaryLookupStub::GeneratePositiveLookup(masm, miss_label, &done,
|
||||
elements, name, r0, r1);
|
||||
|
||||
// If probing finds an entry in the dictionary, r0 contains the
|
||||
// index into the dictionary. Check that the value is a normal
|
||||
// property that is not read only.
|
||||
__ bind(&done);
|
||||
const int kElementsStartOffset =
|
||||
NameDictionary::kHeaderSize +
|
||||
NameDictionary::kElementsStartIndex * kPointerSize;
|
||||
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
||||
const int kTypeAndReadOnlyMask =
|
||||
(PropertyDetails::TypeField::kMask |
|
||||
PropertyDetails::AttributesField::encode(READ_ONLY))
|
||||
<< kSmiTagSize;
|
||||
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
|
||||
Immediate(kTypeAndReadOnlyMask));
|
||||
__ j(not_zero, miss_label);
|
||||
|
||||
// Store the value at the masked, scaled index.
|
||||
const int kValueOffset = kElementsStartOffset + kPointerSize;
|
||||
__ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
|
||||
__ mov(Operand(r0, 0), value);
|
||||
|
||||
// Update write barrier. Make sure not to clobber the value.
|
||||
__ mov(r1, value);
|
||||
__ RecordWrite(elements, r0, r1, kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
||||
Register dictionary = eax;
|
||||
DCHECK(!dictionary.is(LoadDescriptor::ReceiverRegister()));
|
||||
DCHECK(!dictionary.is(LoadDescriptor::NameRegister()));
|
||||
|
||||
Label slow;
|
||||
|
||||
__ mov(dictionary, FieldOperand(LoadDescriptor::ReceiverRegister(),
|
||||
JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryLoad(masm, &slow, dictionary,
|
||||
LoadDescriptor::NameRegister(), edi, ebx, eax);
|
||||
__ ret(0);
|
||||
|
||||
// Dictionary load failed, go slow (but don't miss).
|
||||
__ bind(&slow);
|
||||
GenerateRuntimeGetProperty(masm);
|
||||
}
|
||||
|
||||
void LoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register name = LoadDescriptor::NameRegister();
|
||||
DCHECK(!ebx.is(receiver) && !ebx.is(name));
|
||||
|
||||
__ pop(ebx);
|
||||
__ push(receiver);
|
||||
__ push(name);
|
||||
__ push(ebx);
|
||||
|
||||
// Do tail-call to runtime routine.
|
||||
__ TailCallRuntime(Runtime::kGetProperty);
|
||||
}
|
||||
|
||||
static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
Register receiver = StoreWithVectorDescriptor::ReceiverRegister();
|
||||
Register name = StoreWithVectorDescriptor::NameRegister();
|
||||
@ -171,50 +36,6 @@ static void StoreIC_PushArgs(MacroAssembler* masm) {
|
||||
__ push(return_address);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
||||
// Perform tail call to the entry.
|
||||
__ TailCallRuntime(Runtime::kStoreIC_Miss);
|
||||
}
|
||||
|
||||
|
||||
void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
||||
typedef StoreWithVectorDescriptor Descriptor;
|
||||
Label restore_miss;
|
||||
Register receiver = Descriptor::ReceiverRegister();
|
||||
Register name = Descriptor::NameRegister();
|
||||
Register value = Descriptor::ValueRegister();
|
||||
// Since the slot and vector values are passed on the stack we can use
|
||||
// respective registers as scratch registers.
|
||||
Register scratch1 = Descriptor::VectorRegister();
|
||||
Register scratch2 = Descriptor::SlotRegister();
|
||||
|
||||
__ LoadParameterFromStack<Descriptor>(value, Descriptor::kValue);
|
||||
|
||||
// A lot of registers are needed for storing to slow case objects.
|
||||
// Push and restore receiver but rely on GenerateDictionaryStore preserving
|
||||
// the value and name.
|
||||
__ push(receiver);
|
||||
|
||||
Register dictionary = receiver;
|
||||
__ mov(dictionary, FieldOperand(receiver, JSObject::kPropertiesOffset));
|
||||
GenerateDictionaryStore(masm, &restore_miss, dictionary, name, value,
|
||||
scratch1, scratch2);
|
||||
__ Drop(1);
|
||||
Counters* counters = masm->isolate()->counters();
|
||||
__ IncrementCounter(counters->ic_store_normal_hit(), 1);
|
||||
__ ret(Descriptor::kStackArgumentsCount * kPointerSize);
|
||||
|
||||
__ bind(&restore_miss);
|
||||
__ pop(receiver);
|
||||
__ IncrementCounter(counters->ic_store_normal_miss(), 1);
|
||||
GenerateMiss(masm);
|
||||
}
|
||||
|
||||
|
||||
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
|
||||
// Return address is on the stack.
|
||||
StoreIC_PushArgs(masm);
|
||||
|
@ -2843,85 +2843,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found. Jump to
|
||||
// the |miss| label otherwise.
|
||||
// If lookup was successful |scratch2| will be equal to elements + 4 * index.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!elements.is(scratch1));
|
||||
DCHECK(!elements.is(scratch2));
|
||||
DCHECK(!name.is(scratch1));
|
||||
DCHECK(!name.is(scratch2));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ lw(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ sra(scratch1, scratch1, kSmiTagSize); // convert smi to int
|
||||
__ Subu(scratch1, scratch1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ lw(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ Addu(scratch2, scratch2, Operand(
|
||||
NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ srl(scratch2, scratch2, Name::kHashShift);
|
||||
__ And(scratch2, scratch1, scratch2);
|
||||
|
||||
// Scale the index by multiplying by the element size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
// scratch2 = scratch2 * 3.
|
||||
|
||||
__ Lsa(scratch2, scratch2, scratch2, 1);
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ Lsa(scratch2, elements, scratch2, 2);
|
||||
__ lw(at, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ Branch(done, eq, name, Operand(at));
|
||||
}
|
||||
|
||||
const int spill_mask =
|
||||
(ra.bit() | t2.bit() | t1.bit() | t0.bit() |
|
||||
a3.bit() | a2.bit() | a1.bit() | a0.bit() | v0.bit()) &
|
||||
~(scratch1.bit() | scratch2.bit());
|
||||
|
||||
__ MultiPush(spill_mask);
|
||||
if (name.is(a0)) {
|
||||
DCHECK(!elements.is(a1));
|
||||
__ Move(a1, name);
|
||||
__ Move(a0, elements);
|
||||
} else {
|
||||
__ Move(a0, elements);
|
||||
__ Move(a1, name);
|
||||
}
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ mov(scratch2, a2);
|
||||
__ mov(at, v0);
|
||||
__ MultiPop(spill_mask);
|
||||
|
||||
__ Branch(done, ne, at, Operand(zero_reg));
|
||||
__ Branch(miss, eq, at, Operand(zero_reg));
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -300,14 +300,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -2847,84 +2847,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ Branch(miss, ne, at, Operand(zero_reg));
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found. Jump to
|
||||
// the |miss| label otherwise.
|
||||
// If lookup was successful |scratch2| will be equal to elements + 4 * index.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register scratch1,
|
||||
Register scratch2) {
|
||||
DCHECK(!elements.is(scratch1));
|
||||
DCHECK(!elements.is(scratch2));
|
||||
DCHECK(!name.is(scratch1));
|
||||
DCHECK(!name.is(scratch2));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ ld(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ SmiUntag(scratch1);
|
||||
__ Dsubu(scratch1, scratch1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ lwu(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ Daddu(scratch2, scratch2, Operand(
|
||||
NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ dsrl(scratch2, scratch2, Name::kHashShift);
|
||||
__ And(scratch2, scratch1, scratch2);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
// scratch2 = scratch2 * 3.
|
||||
__ Dlsa(scratch2, scratch2, scratch2, 1);
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ Dlsa(scratch2, elements, scratch2, kPointerSizeLog2);
|
||||
__ ld(at, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ Branch(done, eq, name, Operand(at));
|
||||
}
|
||||
|
||||
const int spill_mask =
|
||||
(ra.bit() | a6.bit() | a5.bit() | a4.bit() |
|
||||
a3.bit() | a2.bit() | a1.bit() | a0.bit() | v0.bit()) &
|
||||
~(scratch1.bit() | scratch2.bit());
|
||||
|
||||
__ MultiPush(spill_mask);
|
||||
if (name.is(a0)) {
|
||||
DCHECK(!elements.is(a1));
|
||||
__ Move(a1, name);
|
||||
__ Move(a0, elements);
|
||||
} else {
|
||||
__ Move(a0, elements);
|
||||
__ Move(a1, name);
|
||||
}
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ mov(scratch2, a2);
|
||||
__ mov(at, v0);
|
||||
__ MultiPop(spill_mask);
|
||||
|
||||
__ Branch(done, ne, at, Operand(zero_reg));
|
||||
__ Branch(miss, eq, at, Operand(zero_reg));
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -301,14 +301,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -2803,84 +2803,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(
|
||||
__ bne(miss);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found. Jump to
|
||||
// the |miss| label otherwise.
|
||||
// If lookup was successful |scratch2| will be equal to elements + 4 * index.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(
|
||||
MacroAssembler* masm, Label* miss, Label* done, Register elements,
|
||||
Register name, Register scratch1, Register scratch2) {
|
||||
DCHECK(!elements.is(scratch1));
|
||||
DCHECK(!elements.is(scratch2));
|
||||
DCHECK(!name.is(scratch1));
|
||||
DCHECK(!name.is(scratch2));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ LoadP(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ SmiUntag(scratch1); // convert smi to int
|
||||
__ subi(scratch1, scratch1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ lwz(scratch2, FieldMemOperand(name, Name::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ addi(scratch2, scratch2,
|
||||
Operand(NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ srwi(scratch2, scratch2, Operand(Name::kHashShift));
|
||||
__ and_(scratch2, scratch1, scratch2);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
// scratch2 = scratch2 * 3.
|
||||
__ ShiftLeftImm(ip, scratch2, Operand(1));
|
||||
__ add(scratch2, scratch2, ip);
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ ShiftLeftImm(ip, scratch2, Operand(kPointerSizeLog2));
|
||||
__ add(scratch2, elements, ip);
|
||||
__ LoadP(ip, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ cmp(name, ip);
|
||||
__ beq(done);
|
||||
}
|
||||
|
||||
const int spill_mask = (r0.bit() | r9.bit() | r8.bit() | r7.bit() | r6.bit() |
|
||||
r5.bit() | r4.bit() | r3.bit()) &
|
||||
~(scratch1.bit() | scratch2.bit());
|
||||
|
||||
__ mflr(r0);
|
||||
__ MultiPush(spill_mask);
|
||||
if (name.is(r3)) {
|
||||
DCHECK(!elements.is(r4));
|
||||
__ mr(r4, name);
|
||||
__ mr(r3, elements);
|
||||
} else {
|
||||
__ mr(r3, elements);
|
||||
__ mr(r4, name);
|
||||
}
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ cmpi(r3, Operand::Zero());
|
||||
__ mr(scratch2, r5);
|
||||
__ MultiPop(spill_mask);
|
||||
__ mtlr(r0);
|
||||
|
||||
__ bne(done);
|
||||
__ beq(miss);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -288,10 +288,6 @@ class NameDictionaryLookupStub : public PlatformCodeStub {
|
||||
Register properties, Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss,
|
||||
Label* done, Register elements,
|
||||
Register name, Register r0, Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -2754,83 +2754,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(
|
||||
__ bne(miss);
|
||||
}
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found. Jump to
|
||||
// the |miss| label otherwise.
|
||||
// If lookup was successful |scratch2| will be equal to elements + 4 * index.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(
|
||||
MacroAssembler* masm, Label* miss, Label* done, Register elements,
|
||||
Register name, Register scratch1, Register scratch2) {
|
||||
DCHECK(!elements.is(scratch1));
|
||||
DCHECK(!elements.is(scratch2));
|
||||
DCHECK(!name.is(scratch1));
|
||||
DCHECK(!name.is(scratch2));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
// Compute the capacity mask.
|
||||
__ LoadP(scratch1, FieldMemOperand(elements, kCapacityOffset));
|
||||
__ SmiUntag(scratch1); // convert smi to int
|
||||
__ SubP(scratch1, Operand(1));
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ LoadlW(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
|
||||
if (i > 0) {
|
||||
// Add the probe offset (i + i * i) left shifted to avoid right shifting
|
||||
// the hash in a separate instruction. The value hash + i + i * i is right
|
||||
// shifted in the following and instruction.
|
||||
DCHECK(NameDictionary::GetProbeOffset(i) <
|
||||
1 << (32 - Name::kHashFieldOffset));
|
||||
__ AddP(scratch2,
|
||||
Operand(NameDictionary::GetProbeOffset(i) << Name::kHashShift));
|
||||
}
|
||||
__ srl(scratch2, Operand(String::kHashShift));
|
||||
__ AndP(scratch2, scratch1);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
// scratch2 = scratch2 * 3.
|
||||
__ ShiftLeftP(ip, scratch2, Operand(1));
|
||||
__ AddP(scratch2, ip);
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ ShiftLeftP(ip, scratch2, Operand(kPointerSizeLog2));
|
||||
__ AddP(scratch2, elements, ip);
|
||||
__ LoadP(ip, FieldMemOperand(scratch2, kElementsStartOffset));
|
||||
__ CmpP(name, ip);
|
||||
__ beq(done);
|
||||
}
|
||||
|
||||
const int spill_mask = (r0.bit() | r8.bit() | r7.bit() | r6.bit() | r5.bit() |
|
||||
r4.bit() | r3.bit() | r2.bit()) &
|
||||
~(scratch1.bit() | scratch2.bit());
|
||||
|
||||
__ LoadRR(r0, r14);
|
||||
__ MultiPush(spill_mask);
|
||||
if (name.is(r2)) {
|
||||
DCHECK(!elements.is(r3));
|
||||
__ LoadRR(r3, name);
|
||||
__ LoadRR(r2, elements);
|
||||
} else {
|
||||
__ LoadRR(r2, elements);
|
||||
__ LoadRR(r3, name);
|
||||
}
|
||||
NameDictionaryLookupStub stub(masm->isolate(), POSITIVE_LOOKUP);
|
||||
__ CallStub(&stub);
|
||||
__ LoadRR(r1, r2);
|
||||
__ LoadRR(scratch2, r4);
|
||||
__ MultiPop(spill_mask);
|
||||
__ LoadRR(r14, r0);
|
||||
|
||||
__ CmpP(r1, Operand::Zero());
|
||||
__ bne(done);
|
||||
__ beq(miss);
|
||||
}
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -312,10 +312,6 @@ class NameDictionaryLookupStub : public PlatformCodeStub {
|
||||
Register properties, Handle<Name> name,
|
||||
Register scratch0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss,
|
||||
Label* done, Register elements,
|
||||
Register name, Register r0, Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -2584,61 +2584,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found leaving the
|
||||
// index into the dictionary in |r1|. Jump to the |miss| label
|
||||
// otherwise.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1) {
|
||||
DCHECK(!elements.is(r0));
|
||||
DCHECK(!elements.is(r1));
|
||||
DCHECK(!name.is(r0));
|
||||
DCHECK(!name.is(r1));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
__ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
|
||||
__ decl(r0);
|
||||
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ movl(r1, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shrl(r1, Immediate(Name::kHashShift));
|
||||
if (i > 0) {
|
||||
__ addl(r1, Immediate(NameDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ andp(r1, r0);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
__ leap(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ cmpp(name, Operand(elements, r1, times_pointer_size,
|
||||
kElementsStartOffset - kHeapObjectTag));
|
||||
__ j(equal, done);
|
||||
}
|
||||
|
||||
NameDictionaryLookupStub stub(masm->isolate(), elements, r0, r1,
|
||||
POSITIVE_LOOKUP);
|
||||
__ Push(name);
|
||||
__ movl(r0, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shrl(r0, Immediate(Name::kHashShift));
|
||||
__ Push(r0);
|
||||
__ CallStub(&stub);
|
||||
|
||||
__ testp(r0, r0);
|
||||
__ j(zero, miss);
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -54,14 +54,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register r0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
@ -2462,67 +2462,6 @@ void NameDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
// Probe the name dictionary in the |elements| register. Jump to the
|
||||
// |done| label if a property with the given name is found leaving the
|
||||
// index into the dictionary in |r0|. Jump to the |miss| label
|
||||
// otherwise.
|
||||
void NameDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1) {
|
||||
DCHECK(!elements.is(r0));
|
||||
DCHECK(!elements.is(r1));
|
||||
DCHECK(!name.is(r0));
|
||||
DCHECK(!name.is(r1));
|
||||
|
||||
__ AssertName(name);
|
||||
|
||||
__ mov(r1, FieldOperand(elements, kCapacityOffset));
|
||||
__ shr(r1, kSmiTagSize); // convert smi to int
|
||||
__ dec(r1);
|
||||
|
||||
// Generate an unrolled loop that performs a few probes before
|
||||
// giving up. Measurements done on Gmail indicate that 2 probes
|
||||
// cover ~93% of loads from dictionaries.
|
||||
for (int i = 0; i < kInlinedProbes; i++) {
|
||||
// Compute the masked index: (hash + i + i * i) & mask.
|
||||
__ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shr(r0, Name::kHashShift);
|
||||
if (i > 0) {
|
||||
__ add(r0, Immediate(NameDictionary::GetProbeOffset(i)));
|
||||
}
|
||||
__ and_(r0, r1);
|
||||
|
||||
// Scale the index by multiplying by the entry size.
|
||||
STATIC_ASSERT(NameDictionary::kEntrySize == 3);
|
||||
__ lea(r0, Operand(r0, r0, times_2, 0)); // r0 = r0 * 3
|
||||
|
||||
// Check if the key is identical to the name.
|
||||
__ cmp(name, Operand(elements,
|
||||
r0,
|
||||
times_4,
|
||||
kElementsStartOffset - kHeapObjectTag));
|
||||
__ j(equal, done);
|
||||
}
|
||||
|
||||
NameDictionaryLookupStub stub(masm->isolate(), elements, r1, r0,
|
||||
POSITIVE_LOOKUP);
|
||||
__ push(name);
|
||||
__ mov(r0, FieldOperand(name, Name::kHashFieldOffset));
|
||||
__ shr(r0, Name::kHashShift);
|
||||
__ push(r0);
|
||||
__ CallStub(&stub);
|
||||
|
||||
__ test(r1, r1);
|
||||
__ j(zero, miss);
|
||||
__ jmp(done);
|
||||
}
|
||||
|
||||
|
||||
void NameDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
// This stub overrides SometimesSetsUpAFrame() to return false. That means
|
||||
// we cannot call anything that could cause a GC from this stub.
|
||||
|
@ -58,14 +58,6 @@ class NameDictionaryLookupStub: public PlatformCodeStub {
|
||||
Handle<Name> name,
|
||||
Register r0);
|
||||
|
||||
static void GeneratePositiveLookup(MacroAssembler* masm,
|
||||
Label* miss,
|
||||
Label* done,
|
||||
Register elements,
|
||||
Register name,
|
||||
Register r0,
|
||||
Register r1);
|
||||
|
||||
bool SometimesSetsUpAFrame() override { return false; }
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user