| Index: src/mips/ic-mips.cc
|
| diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc
|
| index ce51450105c8d1586957535097dba302429481cf..c454d98bebd6cca425328d471721f97041eebd9a 100644
|
| --- a/src/mips/ic-mips.cc
|
| +++ b/src/mips/ic-mips.cc
|
| @@ -48,52 +48,765 @@ namespace internal {
|
| #define __ ACCESS_MASM(masm)
|
|
|
|
|
| +static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
|
| + Register type,
|
| + Label* global_object) {
|
| + // Register usage:
|
| + // type: holds the receiver instance type on entry.
|
| + __ Branch(global_object, eq, type, Operand(JS_GLOBAL_OBJECT_TYPE));
|
| + __ Branch(global_object, eq, type, Operand(JS_BUILTINS_OBJECT_TYPE));
|
| + __ Branch(global_object, eq, type, Operand(JS_GLOBAL_PROXY_TYPE));
|
| +}
|
| +
|
| +
|
| +// Generated code falls through if the receiver is a regular non-global
|
| +// JS object with slow properties and no interceptors.
|
| +static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register elements,
|
| + Register scratch0,
|
| + Register scratch1,
|
| + Label* miss) {
|
| + // Register usage:
|
| + // receiver: holds the receiver on entry and is unchanged.
|
| + // elements: holds the property dictionary on fall through.
|
| + // Scratch registers:
|
| + // scratch0: used to holds the receiver map.
|
| + // scratch1: used to holds the receiver instance type, receiver bit mask
|
| + // and elements map.
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(receiver, miss);
|
| +
|
| + // Check that the receiver is a valid JS object.
|
| + __ GetObjectType(receiver, scratch0, scratch1);
|
| + __ Branch(miss, lt, scratch1, Operand(FIRST_JS_OBJECT_TYPE));
|
| +
|
| + // If this assert fails, we have to check upper bound too.
|
| + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
|
| +
|
| + GenerateGlobalInstanceTypeCheck(masm, scratch1, miss);
|
| +
|
| + // Check that the global object does not require access checks.
|
| + __ lbu(scratch1, FieldMemOperand(scratch0, Map::kBitFieldOffset));
|
| + __ And(scratch1, scratch1, Operand((1 << Map::kIsAccessCheckNeeded) |
|
| + (1 << Map::kHasNamedInterceptor)));
|
| + __ Branch(miss, ne, scratch1, Operand(zero_reg));
|
| +
|
| + __ lw(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
|
| + __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
|
| + __ LoadRoot(scratch0, Heap::kHashTableMapRootIndex);
|
| + __ Branch(miss, ne, scratch1, Operand(scratch0));
|
| +}
|
| +
|
| +
|
| +// Helper function used from LoadIC/CallIC 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.
|
| + StringDictionaryLookupStub::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 = StringDictionary::kHeaderSize +
|
| + StringDictionary::kElementsStartIndex * kPointerSize;
|
| + const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
| + __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
|
| + __ And(at,
|
| + scratch1,
|
| + Operand(PropertyDetails::TypeField::mask() << 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.
|
| + StringDictionaryLookupStub::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 = StringDictionary::kHeaderSize +
|
| + StringDictionary::kElementsStartIndex * kPointerSize;
|
| + const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
|
| + const int kTypeAndReadOnlyMask
|
| + = (PropertyDetails::TypeField::mask() |
|
| + 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);
|
| +}
|
| +
|
| +
|
| +static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
|
| + Label* miss,
|
| + Register elements,
|
| + Register key,
|
| + Register result,
|
| + Register reg0,
|
| + Register reg1,
|
| + Register reg2) {
|
| + // Register use:
|
| + //
|
| + // elements - holds the slow-case elements of the receiver on entry.
|
| + // Unchanged unless 'result' is the same register.
|
| + //
|
| + // key - holds the smi key on entry.
|
| + // Unchanged unless 'result' is the same register.
|
| + //
|
| + //
|
| + // result - holds the result on exit if the load succeeded.
|
| + // Allowed to be the same as 'key' or 'result'.
|
| + // Unchanged on bailout so 'key' or 'result' can be used
|
| + // in further computation.
|
| + //
|
| + // Scratch registers:
|
| + //
|
| + // reg0 - holds the untagged key on entry and holds the hash once computed.
|
| + //
|
| + // reg1 - Used to hold the capacity mask of the dictionary.
|
| + //
|
| + // reg2 - Used for the index into the dictionary.
|
| + // at - Temporary (avoid MacroAssembler instructions also using 'at').
|
| + Label done;
|
| +
|
| + // Compute the hash code from the untagged key. This must be kept in sync
|
| + // with ComputeIntegerHash in utils.h.
|
| + //
|
| + // hash = ~hash + (hash << 15);
|
| + __ nor(reg1, reg0, zero_reg);
|
| + __ sll(at, reg0, 15);
|
| + __ addu(reg0, reg1, at);
|
| +
|
| + // hash = hash ^ (hash >> 12);
|
| + __ srl(at, reg0, 12);
|
| + __ xor_(reg0, reg0, at);
|
| +
|
| + // hash = hash + (hash << 2);
|
| + __ sll(at, reg0, 2);
|
| + __ addu(reg0, reg0, at);
|
| +
|
| + // hash = hash ^ (hash >> 4);
|
| + __ srl(at, reg0, 4);
|
| + __ xor_(reg0, reg0, at);
|
| +
|
| + // hash = hash * 2057;
|
| + __ li(reg1, Operand(2057));
|
| + __ mul(reg0, reg0, reg1);
|
| +
|
| + // hash = hash ^ (hash >> 16);
|
| + __ srl(at, reg0, 16);
|
| + __ xor_(reg0, reg0, at);
|
| +
|
| + // Compute the capacity mask.
|
| + __ lw(reg1, FieldMemOperand(elements, NumberDictionary::kCapacityOffset));
|
| + __ sra(reg1, reg1, kSmiTagSize);
|
| + __ Subu(reg1, reg1, Operand(1));
|
| +
|
| + // Generate an unrolled loop that performs a few probes before giving up.
|
| + static const int kProbes = 4;
|
| + for (int i = 0; i < kProbes; i++) {
|
| + // Use reg2 for index calculations and keep the hash intact in reg0.
|
| + __ mov(reg2, reg0);
|
| + // Compute the masked index: (hash + i + i * i) & mask.
|
| + if (i > 0) {
|
| + __ Addu(reg2, reg2, Operand(NumberDictionary::GetProbeOffset(i)));
|
| + }
|
| + __ and_(reg2, reg2, reg1);
|
| +
|
| + // Scale the index by multiplying by the element size.
|
| + ASSERT(NumberDictionary::kEntrySize == 3);
|
| + __ sll(at, reg2, 1); // 2x.
|
| + __ addu(reg2, reg2, at); // reg2 = reg2 * 3.
|
| +
|
| + // Check if the key is identical to the name.
|
| + __ sll(at, reg2, kPointerSizeLog2);
|
| + __ addu(reg2, elements, at);
|
| +
|
| + __ lw(at, FieldMemOperand(reg2, NumberDictionary::kElementsStartOffset));
|
| + if (i != kProbes - 1) {
|
| + __ Branch(&done, eq, key, Operand(at));
|
| + } else {
|
| + __ Branch(miss, ne, key, Operand(at));
|
| + }
|
| + }
|
| +
|
| + __ bind(&done);
|
| + // Check that the value is a normal property.
|
| + // reg2: elements + (index * kPointerSize).
|
| + const int kDetailsOffset =
|
| + NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
|
| + __ lw(reg1, FieldMemOperand(reg2, kDetailsOffset));
|
| + __ And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask())));
|
| + __ Branch(miss, ne, at, Operand(zero_reg));
|
| +
|
| + // Get the value at the masked, scaled index and return.
|
| + const int kValueOffset =
|
| + NumberDictionary::kElementsStartOffset + kPointerSize;
|
| + __ lw(result, FieldMemOperand(reg2, kValueOffset));
|
| +}
|
| +
|
| +
|
| void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + StubCompiler::GenerateLoadArrayLength(masm, a0, a3, &miss);
|
| + __ bind(&miss);
|
| + StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
|
| }
|
|
|
|
|
| void LoadIC::GenerateStringLength(MacroAssembler* masm, bool support_wrappers) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- lr : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + StubCompiler::GenerateLoadStringLength(masm, a0, a1, a3, &miss,
|
| + support_wrappers);
|
| + // Cache miss: Jump to runtime.
|
| + __ bind(&miss);
|
| + StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
|
| }
|
|
|
|
|
| void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- lr : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + StubCompiler::GenerateLoadFunctionPrototype(masm, a0, a1, a3, &miss);
|
| + __ bind(&miss);
|
| + StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
|
| +}
|
| +
|
| +
|
| +// Checks the receiver for special cases (value type, slow case bits).
|
| +// Falls through for regular JS object.
|
| +static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register map,
|
| + Register scratch,
|
| + int interceptor_bit,
|
| + Label* slow) {
|
| + // Check that the object isn't a smi.
|
| + __ JumpIfSmi(receiver, slow);
|
| + // Get the map of the receiver.
|
| + __ lw(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
| + // Check bit field.
|
| + __ lbu(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
|
| + __ And(at, scratch, Operand(KeyedLoadIC::kSlowCaseBitFieldMask));
|
| + __ Branch(slow, ne, at, Operand(zero_reg));
|
| + // Check that the object is some kind of JS object EXCEPT JS Value type.
|
| + // In the case that the object is a value-wrapper object,
|
| + // we enter the runtime system to make sure that indexing into string
|
| + // objects work as intended.
|
| + ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
|
| + __ lbu(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
|
| + __ Branch(slow, lt, scratch, Operand(JS_OBJECT_TYPE));
|
| +}
|
| +
|
| +
|
| +// Loads an indexed element from a fast case array.
|
| +// If not_fast_array is NULL, doesn't perform the elements map check.
|
| +static void GenerateFastArrayLoad(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register key,
|
| + Register elements,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register result,
|
| + Label* not_fast_array,
|
| + Label* out_of_range) {
|
| + // Register use:
|
| + //
|
| + // receiver - holds the receiver on entry.
|
| + // Unchanged unless 'result' is the same register.
|
| + //
|
| + // key - holds the smi key on entry.
|
| + // Unchanged unless 'result' is the same register.
|
| + //
|
| + // elements - holds the elements of the receiver on exit.
|
| + //
|
| + // result - holds the result on exit if the load succeeded.
|
| + // Allowed to be the the same as 'receiver' or 'key'.
|
| + // Unchanged on bailout so 'receiver' and 'key' can be safely
|
| + // used by further computation.
|
| + //
|
| + // Scratch registers:
|
| + //
|
| + // scratch1 - used to hold elements map and elements length.
|
| + // Holds the elements map if not_fast_array branch is taken.
|
| + //
|
| + // scratch2 - used to hold the loaded value.
|
| +
|
| + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
| + if (not_fast_array != NULL) {
|
| + // Check that the object is in fast mode (not dictionary).
|
| + __ lw(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
|
| + __ LoadRoot(at, Heap::kFixedArrayMapRootIndex);
|
| + __ Branch(not_fast_array, ne, scratch1, Operand(at));
|
| + } else {
|
| + __ AssertFastElements(elements);
|
| + }
|
| +
|
| + // Check that the key (index) is within bounds.
|
| + __ lw(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
| + __ Branch(out_of_range, hs, key, Operand(scratch1));
|
| +
|
| + // Fast case: Do the load.
|
| + __ Addu(scratch1, elements,
|
| + Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
| + // The key is a smi.
|
| + ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
|
| + __ sll(at, key, kPointerSizeLog2 - kSmiTagSize);
|
| + __ addu(at, at, scratch1);
|
| + __ lw(scratch2, MemOperand(at));
|
| +
|
| + __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
|
| + // In case the loaded value is the_hole we have to consult GetProperty
|
| + // to ensure the prototype chain is searched.
|
| + __ Branch(out_of_range, eq, scratch2, Operand(at));
|
| + __ mov(result, scratch2);
|
| +}
|
| +
|
| +
|
| +// Checks whether a key is an array index string or a symbol string.
|
| +// Falls through if a key is a symbol.
|
| +static void GenerateKeyStringCheck(MacroAssembler* masm,
|
| + Register key,
|
| + Register map,
|
| + Register hash,
|
| + Label* index_string,
|
| + Label* not_symbol) {
|
| + // The key is not a smi.
|
| + // Is it a string?
|
| + __ GetObjectType(key, map, hash);
|
| + __ Branch(not_symbol, ge, hash, Operand(FIRST_NONSTRING_TYPE));
|
| +
|
| + // Is the string an array index, with cached numeric value?
|
| + __ lw(hash, FieldMemOperand(key, String::kHashFieldOffset));
|
| + __ And(at, hash, Operand(String::kContainsCachedArrayIndexMask));
|
| + __ Branch(index_string, eq, at, Operand(zero_reg));
|
| +
|
| + // Is the string a symbol?
|
| + // map: key map
|
| + __ lbu(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
|
| + ASSERT(kSymbolTag != 0);
|
| + __ And(at, hash, Operand(kIsSymbolMask));
|
| + __ Branch(not_symbol, eq, at, Operand(zero_reg));
|
| }
|
|
|
|
|
| // Defined in ic.cc.
|
| Object* CallIC_Miss(Arguments args);
|
|
|
| +// The generated code does not accept smi keys.
|
| +// The generated code falls through if both probes miss.
|
| +static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
|
| + int argc,
|
| + Code::Kind kind) {
|
| + // ----------- S t a t e -------------
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -----------------------------------
|
| + Label number, non_number, non_string, boolean, probe, miss;
|
| +
|
| + // Probe the stub cache.
|
| + Code::Flags flags = Code::ComputeFlags(kind,
|
| + NOT_IN_LOOP,
|
| + MONOMORPHIC,
|
| + Code::kNoExtraICState,
|
| + NORMAL,
|
| + argc);
|
| + Isolate::Current()->stub_cache()->GenerateProbe(
|
| + masm, flags, a1, a2, a3, t0, t1);
|
| +
|
| + // If the stub cache probing failed, the receiver might be a value.
|
| + // For value objects, we use the map of the prototype objects for
|
| + // the corresponding JSValue for the cache and that is what we need
|
| + // to probe.
|
| + //
|
| + // Check for number.
|
| + __ JumpIfSmi(a1, &number, t1);
|
| + __ GetObjectType(a1, a3, a3);
|
| + __ Branch(&non_number, ne, a3, Operand(HEAP_NUMBER_TYPE));
|
| + __ bind(&number);
|
| + StubCompiler::GenerateLoadGlobalFunctionPrototype(
|
| + masm, Context::NUMBER_FUNCTION_INDEX, a1);
|
| + __ Branch(&probe);
|
| +
|
| + // Check for string.
|
| + __ bind(&non_number);
|
| + __ Branch(&non_string, Ugreater_equal, a3, Operand(FIRST_NONSTRING_TYPE));
|
| + StubCompiler::GenerateLoadGlobalFunctionPrototype(
|
| + masm, Context::STRING_FUNCTION_INDEX, a1);
|
| + __ Branch(&probe);
|
| +
|
| + // Check for boolean.
|
| + __ bind(&non_string);
|
| + __ LoadRoot(t0, Heap::kTrueValueRootIndex);
|
| + __ Branch(&boolean, eq, a1, Operand(t0));
|
| + __ LoadRoot(t1, Heap::kFalseValueRootIndex);
|
| + __ Branch(&miss, ne, a1, Operand(t1));
|
| + __ bind(&boolean);
|
| + StubCompiler::GenerateLoadGlobalFunctionPrototype(
|
| + masm, Context::BOOLEAN_FUNCTION_INDEX, a1);
|
| +
|
| + // Probe the stub cache for the value object.
|
| + __ bind(&probe);
|
| + Isolate::Current()->stub_cache()->GenerateProbe(
|
| + masm, flags, a1, a2, a3, t0, t1);
|
| +
|
| + __ bind(&miss);
|
| +}
|
| +
|
| +
|
| +static void GenerateFunctionTailCall(MacroAssembler* masm,
|
| + int argc,
|
| + Label* miss,
|
| + Register scratch) {
|
| + // a1: function
|
| +
|
| + // Check that the value isn't a smi.
|
| + __ JumpIfSmi(a1, miss);
|
| +
|
| + // Check that the value is a JSFunction.
|
| + __ GetObjectType(a1, scratch, scratch);
|
| + __ Branch(miss, ne, scratch, Operand(JS_FUNCTION_TYPE));
|
| +
|
| + // Invoke the function.
|
| + ParameterCount actual(argc);
|
| + __ InvokeFunction(a1, actual, JUMP_FUNCTION);
|
| +}
|
| +
|
| +
|
| +static void GenerateCallNormal(MacroAssembler* masm, int argc) {
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + // Get the receiver of the function from the stack into a1.
|
| + __ lw(a1, MemOperand(sp, argc * kPointerSize));
|
| +
|
| + GenerateStringDictionaryReceiverCheck(masm, a1, a0, a3, t0, &miss);
|
| +
|
| + // a0: elements
|
| + // Search the dictionary - put result in register a1.
|
| + GenerateDictionaryLoad(masm, &miss, a0, a2, a1, a3, t0);
|
| +
|
| + GenerateFunctionTailCall(masm, argc, &miss, t0);
|
| +
|
| + // Cache miss: Jump to runtime.
|
| + __ bind(&miss);
|
| +}
|
| +
|
| +
|
| +static void GenerateCallMiss(MacroAssembler* masm, int argc, IC::UtilityId id) {
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| + Isolate* isolate = masm->isolate();
|
| +
|
| + if (id == IC::kCallIC_Miss) {
|
| + __ IncrementCounter(isolate->counters()->call_miss(), 1, a3, t0);
|
| + } else {
|
| + __ IncrementCounter(isolate->counters()->keyed_call_miss(), 1, a3, t0);
|
| + }
|
| +
|
| + // Get the receiver of the function from the stack.
|
| + __ lw(a3, MemOperand(sp, argc*kPointerSize));
|
| +
|
| + __ EnterInternalFrame();
|
| +
|
| + // Push the receiver and the name of the function.
|
| + __ Push(a3, a2);
|
| +
|
| + // Call the entry.
|
| + __ li(a0, Operand(2));
|
| + __ li(a1, Operand(ExternalReference(IC_Utility(id), isolate)));
|
| +
|
| + CEntryStub stub(1);
|
| + __ CallStub(&stub);
|
| +
|
| + // Move result to a1 and leave the internal frame.
|
| + __ mov(a1, v0);
|
| + __ LeaveInternalFrame();
|
| +
|
| + // Check if the receiver is a global object of some sort.
|
| + // This can happen only for regular CallIC but not KeyedCallIC.
|
| + if (id == IC::kCallIC_Miss) {
|
| + Label invoke, global;
|
| + __ lw(a2, MemOperand(sp, argc * kPointerSize));
|
| + __ andi(t0, a2, kSmiTagMask);
|
| + __ Branch(&invoke, eq, t0, Operand(zero_reg));
|
| + __ GetObjectType(a2, a3, a3);
|
| + __ Branch(&global, eq, a3, Operand(JS_GLOBAL_OBJECT_TYPE));
|
| + __ Branch(&invoke, ne, a3, Operand(JS_BUILTINS_OBJECT_TYPE));
|
| +
|
| + // Patch the receiver on the stack.
|
| + __ bind(&global);
|
| + __ lw(a2, FieldMemOperand(a2, GlobalObject::kGlobalReceiverOffset));
|
| + __ sw(a2, MemOperand(sp, argc * kPointerSize));
|
| + __ bind(&invoke);
|
| + }
|
| + // Invoke the function.
|
| + ParameterCount actual(argc);
|
| + __ InvokeFunction(a1, actual, JUMP_FUNCTION);
|
| +}
|
| +
|
|
|
| void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + GenerateCallMiss(masm, argc, IC::kCallIC_Miss);
|
| }
|
|
|
|
|
| void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Get the receiver of the function from the stack into a1.
|
| + __ lw(a1, MemOperand(sp, argc * kPointerSize));
|
| + GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC);
|
| + GenerateMiss(masm, argc);
|
| }
|
|
|
|
|
| void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + GenerateCallNormal(masm, argc);
|
| + GenerateMiss(masm, argc);
|
| }
|
|
|
|
|
| void KeyedCallIC::GenerateMiss(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + GenerateCallMiss(masm, argc, IC::kKeyedCallIC_Miss);
|
| }
|
|
|
|
|
| void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Get the receiver of the function from the stack into a1.
|
| + __ lw(a1, MemOperand(sp, argc * kPointerSize));
|
| +
|
| + Label do_call, slow_call, slow_load, slow_reload_receiver;
|
| + Label check_number_dictionary, check_string, lookup_monomorphic_cache;
|
| + Label index_smi, index_string;
|
| +
|
| + // Check that the key is a smi.
|
| + __ JumpIfNotSmi(a2, &check_string);
|
| + __ bind(&index_smi);
|
| + // Now the key is known to be a smi. This place is also jumped to from below
|
| + // where a numeric string is converted to a smi.
|
| +
|
| + GenerateKeyedLoadReceiverCheck(
|
| + masm, a1, a0, a3, Map::kHasIndexedInterceptor, &slow_call);
|
| +
|
| + GenerateFastArrayLoad(
|
| + masm, a1, a2, t0, a3, a0, a1, &check_number_dictionary, &slow_load);
|
| + Counters* counters = masm->isolate()->counters();
|
| + __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1, a0, a3);
|
| +
|
| + __ bind(&do_call);
|
| + // receiver in a1 is not used after this point.
|
| + // a2: key
|
| + // a1: function
|
| +
|
| + GenerateFunctionTailCall(masm, argc, &slow_call, a0);
|
| +
|
| + __ bind(&check_number_dictionary);
|
| + // a2: key
|
| + // a3: elements map
|
| + // t0: elements pointer
|
| + // Check whether the elements is a number dictionary.
|
| + __ LoadRoot(at, Heap::kHashTableMapRootIndex);
|
| + __ Branch(&slow_load, ne, a3, Operand(at));
|
| + __ sra(a0, a2, kSmiTagSize);
|
| + // a0: untagged index
|
| + GenerateNumberDictionaryLoad(masm, &slow_load, t0, a2, a1, a0, a3, t1);
|
| + __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, a0, a3);
|
| + __ jmp(&do_call);
|
| +
|
| + __ bind(&slow_load);
|
| + // This branch is taken when calling KeyedCallIC_Miss is neither required
|
| + // nor beneficial.
|
| + __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, a0, a3);
|
| + __ EnterInternalFrame();
|
| + __ push(a2); // Save the key.
|
| + __ Push(a1, a2); // Pass the receiver and the key.
|
| + __ CallRuntime(Runtime::kKeyedGetProperty, 2);
|
| + __ pop(a2); // Restore the key.
|
| + __ LeaveInternalFrame();
|
| + __ mov(a1, v0);
|
| + __ jmp(&do_call);
|
| +
|
| + __ bind(&check_string);
|
| + GenerateKeyStringCheck(masm, a2, a0, a3, &index_string, &slow_call);
|
| +
|
| + // The key is known to be a symbol.
|
| + // If the receiver is a regular JS object with slow properties then do
|
| + // a quick inline probe of the receiver's dictionary.
|
| + // Otherwise do the monomorphic cache probe.
|
| + GenerateKeyedLoadReceiverCheck(
|
| + masm, a1, a0, a3, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
|
| +
|
| + __ lw(a0, FieldMemOperand(a1, JSObject::kPropertiesOffset));
|
| + __ lw(a3, FieldMemOperand(a0, HeapObject::kMapOffset));
|
| + __ LoadRoot(at, Heap::kHashTableMapRootIndex);
|
| + __ Branch(&lookup_monomorphic_cache, ne, a3, Operand(at));
|
| +
|
| + GenerateDictionaryLoad(masm, &slow_load, a0, a2, a1, a3, t0);
|
| + __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1, a0, a3);
|
| + __ jmp(&do_call);
|
| +
|
| + __ bind(&lookup_monomorphic_cache);
|
| + __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, a0, a3);
|
| + GenerateMonomorphicCacheProbe(masm, argc, Code::KEYED_CALL_IC);
|
| + // Fall through on miss.
|
| +
|
| + __ bind(&slow_call);
|
| + // This branch is taken if:
|
| + // - the receiver requires boxing or access check,
|
| + // - the key is neither smi nor symbol,
|
| + // - the value loaded is not a function,
|
| + // - there is hope that the runtime will create a monomorphic call stub,
|
| + // that will get fetched next time.
|
| + __ IncrementCounter(counters->keyed_call_generic_slow(), 1, a0, a3);
|
| + GenerateMiss(masm, argc);
|
| +
|
| + __ bind(&index_string);
|
| + __ IndexFromHash(a3, a2);
|
| + // Now jump to the place where smi keys are handled.
|
| + __ jmp(&index_smi);
|
| }
|
|
|
|
|
| void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Check if the name is a string.
|
| + Label miss;
|
| + __ JumpIfSmi(a2, &miss);
|
| + __ IsObjectJSStringType(a2, a0, &miss);
|
| +
|
| + GenerateCallNormal(masm, argc);
|
| + __ bind(&miss);
|
| + GenerateMiss(masm, argc);
|
| }
|
|
|
|
|
| @@ -101,17 +814,63 @@ void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
|
| Object* LoadIC_Miss(Arguments args);
|
|
|
| void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| +
|
| + // Probe the stub cache.
|
| + Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
|
| + NOT_IN_LOOP,
|
| + MONOMORPHIC);
|
| + Isolate::Current()->stub_cache()->GenerateProbe(
|
| + masm, flags, a0, a2, a3, t0, t1);
|
| +
|
| + // Cache miss: Jump to runtime.
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void LoadIC::GenerateNormal(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- lr : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + GenerateStringDictionaryReceiverCheck(masm, a0, a1, a3, t0, &miss);
|
| +
|
| + // a1: elements
|
| + GenerateDictionaryLoad(masm, &miss, a1, a2, v0, a3, t0);
|
| + __ Ret();
|
| +
|
| + // Cache miss: Jump to runtime.
|
| + __ bind(&miss);
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void LoadIC::GenerateMiss(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -- a0 : receiver
|
| + // -- sp[0] : receiver
|
| + // -----------------------------------
|
| + Isolate* isolate = masm->isolate();
|
| +
|
| + __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
|
| +
|
| + __ mov(a3, a0);
|
| + __ Push(a3, a2);
|
| +
|
| + // Perform tail call to the entry.
|
| + ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
|
| + __ TailCallExternalReference(ref, 2, 1);
|
| }
|
|
|
|
|
| @@ -119,71 +878,540 @@ Object* KeyedLoadIC_Miss(Arguments args);
|
|
|
|
|
| void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- ra : return address
|
| + // -- a0 : key
|
| + // -- a1 : receiver
|
| + // -----------------------------------
|
| + Isolate* isolate = masm->isolate();
|
| +
|
| + __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, a3, t0);
|
| +
|
| + __ Push(a1, a0);
|
| +
|
| + ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss),
|
| + isolate);
|
| + __ TailCallExternalReference(ref, 2, 1);
|
| }
|
|
|
|
|
| void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- ra : return address
|
| + // -- a0 : key
|
| + // -- a1 : receiver
|
| + // -----------------------------------
|
| +
|
| + __ Push(a1, a0);
|
| +
|
| + __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
|
| }
|
|
|
|
|
| void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- ra : return address
|
| + // -- a0 : key
|
| + // -- a1 : receiver
|
| + // -----------------------------------
|
| + Label slow, check_string, index_smi, index_string, property_array_property;
|
| + Label probe_dictionary, check_number_dictionary;
|
| +
|
| + Register key = a0;
|
| + Register receiver = a1;
|
| +
|
| + Isolate* isolate = masm->isolate();
|
| +
|
| + // Check that the key is a smi.
|
| + __ JumpIfNotSmi(key, &check_string);
|
| + __ bind(&index_smi);
|
| + // Now the key is known to be a smi. This place is also jumped to from below
|
| + // where a numeric string is converted to a smi.
|
| +
|
| + GenerateKeyedLoadReceiverCheck(
|
| + masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
|
| +
|
| + // Check the "has fast elements" bit in the receiver's map which is
|
| + // now in a2.
|
| + __ lbu(a3, FieldMemOperand(a2, Map::kBitField2Offset));
|
| + __ And(at, a3, Operand(1 << Map::kHasFastElements));
|
| + __ Branch(&check_number_dictionary, eq, at, Operand(zero_reg));
|
| +
|
| + GenerateFastArrayLoad(
|
| + masm, receiver, key, t0, a3, a2, v0, NULL, &slow);
|
| +
|
| + __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, a2, a3);
|
| + __ Ret();
|
| +
|
| + __ bind(&check_number_dictionary);
|
| + __ lw(t0, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
| + __ lw(a3, FieldMemOperand(t0, JSObject::kMapOffset));
|
| +
|
| + // Check whether the elements is a number dictionary.
|
| + // a0: key
|
| + // a3: elements map
|
| + // t0: elements
|
| + __ LoadRoot(at, Heap::kHashTableMapRootIndex);
|
| + __ Branch(&slow, ne, a3, Operand(at));
|
| + __ sra(a2, a0, kSmiTagSize);
|
| + GenerateNumberDictionaryLoad(masm, &slow, t0, a0, v0, a2, a3, t1);
|
| + __ Ret();
|
| +
|
| + // Slow case, key and receiver still in a0 and a1.
|
| + __ bind(&slow);
|
| + __ IncrementCounter(isolate->counters()->keyed_load_generic_slow(),
|
| + 1,
|
| + a2,
|
| + a3);
|
| + GenerateRuntimeGetProperty(masm);
|
| +
|
| + __ bind(&check_string);
|
| + GenerateKeyStringCheck(masm, key, a2, a3, &index_string, &slow);
|
| +
|
| + GenerateKeyedLoadReceiverCheck(
|
| + masm, receiver, a2, a3, Map::kHasIndexedInterceptor, &slow);
|
| +
|
| +
|
| + // If the receiver is a fast-case object, check the keyed lookup
|
| + // cache. Otherwise probe the dictionary.
|
| + __ lw(a3, FieldMemOperand(a1, JSObject::kPropertiesOffset));
|
| + __ lw(t0, FieldMemOperand(a3, HeapObject::kMapOffset));
|
| + __ LoadRoot(at, Heap::kHashTableMapRootIndex);
|
| + __ Branch(&probe_dictionary, eq, t0, Operand(at));
|
| +
|
| + // Load the map of the receiver, compute the keyed lookup cache hash
|
| + // based on 32 bits of the map pointer and the string hash.
|
| + __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
|
| + __ sra(a3, a2, KeyedLookupCache::kMapHashShift);
|
| + __ lw(t0, FieldMemOperand(a0, String::kHashFieldOffset));
|
| + __ sra(at, t0, String::kHashShift);
|
| + __ xor_(a3, a3, at);
|
| + __ And(a3, a3, Operand(KeyedLookupCache::kCapacityMask));
|
| +
|
| + // Load the key (consisting of map and symbol) from the cache and
|
| + // check for match.
|
| + ExternalReference cache_keys =
|
| + ExternalReference::keyed_lookup_cache_keys(isolate);
|
| + __ li(t0, Operand(cache_keys));
|
| + __ sll(at, a3, kPointerSizeLog2 + 1);
|
| + __ addu(t0, t0, at);
|
| + __ lw(t1, MemOperand(t0)); // Move t0 to symbol.
|
| + __ Addu(t0, t0, Operand(kPointerSize));
|
| + __ Branch(&slow, ne, a2, Operand(t1));
|
| + __ lw(t1, MemOperand(t0));
|
| + __ Branch(&slow, ne, a0, Operand(t1));
|
| +
|
| + // Get field offset.
|
| + // a0 : key
|
| + // a1 : receiver
|
| + // a2 : receiver's map
|
| + // a3 : lookup cache index
|
| + ExternalReference cache_field_offsets =
|
| + ExternalReference::keyed_lookup_cache_field_offsets(isolate);
|
| + __ li(t0, Operand(cache_field_offsets));
|
| + __ sll(at, a3, kPointerSizeLog2);
|
| + __ addu(at, t0, at);
|
| + __ lw(t1, MemOperand(at));
|
| + __ lbu(t2, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
|
| + __ Subu(t1, t1, t2);
|
| + __ Branch(&property_array_property, ge, t1, Operand(zero_reg));
|
| +
|
| + // Load in-object property.
|
| + __ lbu(t2, FieldMemOperand(a2, Map::kInstanceSizeOffset));
|
| + __ addu(t2, t2, t1); // Index from start of object.
|
| + __ Subu(a1, a1, Operand(kHeapObjectTag)); // Remove the heap tag.
|
| + __ sll(at, t2, kPointerSizeLog2);
|
| + __ addu(at, a1, at);
|
| + __ lw(v0, MemOperand(at));
|
| + __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
|
| + 1,
|
| + a2,
|
| + a3);
|
| + __ Ret();
|
| +
|
| + // Load property array property.
|
| + __ bind(&property_array_property);
|
| + __ lw(a1, FieldMemOperand(a1, JSObject::kPropertiesOffset));
|
| + __ Addu(a1, a1, FixedArray::kHeaderSize - kHeapObjectTag);
|
| + __ sll(t0, t1, kPointerSizeLog2);
|
| + __ Addu(t0, t0, a1);
|
| + __ lw(v0, MemOperand(t0));
|
| + __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
|
| + 1,
|
| + a2,
|
| + a3);
|
| + __ Ret();
|
| +
|
| +
|
| + // Do a quick inline probe of the receiver's dictionary, if it
|
| + // exists.
|
| + __ bind(&probe_dictionary);
|
| + // a1: receiver
|
| + // a0: key
|
| + // a3: elements
|
| + __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
|
| + __ lbu(a2, FieldMemOperand(a2, Map::kInstanceTypeOffset));
|
| + GenerateGlobalInstanceTypeCheck(masm, a2, &slow);
|
| + // Load the property to v0.
|
| + GenerateDictionaryLoad(masm, &slow, a3, a0, v0, a2, t0);
|
| + __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(),
|
| + 1,
|
| + a2,
|
| + a3);
|
| + __ Ret();
|
| +
|
| + __ bind(&index_string);
|
| + __ IndexFromHash(a3, key);
|
| + // Now jump to the place where smi keys are handled.
|
| + __ Branch(&index_smi);
|
| }
|
|
|
|
|
| void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- ra : return address
|
| + // -- a0 : key (index)
|
| + // -- a1 : receiver
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + Register receiver = a1;
|
| + Register index = a0;
|
| + Register scratch1 = a2;
|
| + Register scratch2 = a3;
|
| + Register result = v0;
|
| +
|
| + StringCharAtGenerator char_at_generator(receiver,
|
| + index,
|
| + scratch1,
|
| + scratch2,
|
| + result,
|
| + &miss, // When not a string.
|
| + &miss, // When not a number.
|
| + &miss, // When index out of range.
|
| + STRING_INDEX_IS_ARRAY_INDEX);
|
| + char_at_generator.GenerateFast(masm);
|
| + __ Ret();
|
| +
|
| + StubRuntimeCallHelper call_helper;
|
| + char_at_generator.GenerateSlow(masm, call_helper);
|
| +
|
| + __ bind(&miss);
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
|
| StrictModeFlag strict_mode) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- a0 : value
|
| + // -- a1 : key
|
| + // -- a2 : receiver
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Push receiver, key and value for runtime call.
|
| + __ Push(a2, a1, a0);
|
| + __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
|
| + __ li(a0, Operand(Smi::FromInt(strict_mode))); // Strict mode.
|
| + __ Push(a1, a0);
|
| +
|
| + __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
|
| }
|
|
|
|
|
| void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
| StrictModeFlag strict_mode) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- a0 : value
|
| + // -- a1 : key
|
| + // -- a2 : receiver
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + Label slow, fast, array, extra, exit;
|
| +
|
| + // Register usage.
|
| + Register value = a0;
|
| + Register key = a1;
|
| + Register receiver = a2;
|
| + Register elements = a3; // Elements array of the receiver.
|
| + // t0 is used as ip in the arm version.
|
| + // t3-t4 are used as temporaries.
|
| +
|
| + // Check that the key is a smi.
|
| + __ JumpIfNotSmi(key, &slow);
|
| + // Check that the object isn't a smi.
|
| + __ JumpIfSmi(receiver, &slow);
|
| +
|
| + // Get the map of the object.
|
| + __ lw(t3, FieldMemOperand(receiver, HeapObject::kMapOffset));
|
| + // Check that the receiver does not require access checks. We need
|
| + // to do this because this generic stub does not perform map checks.
|
| + __ lbu(t0, FieldMemOperand(t3, Map::kBitFieldOffset));
|
| + __ And(t0, t0, Operand(1 << Map::kIsAccessCheckNeeded));
|
| + __ Branch(&slow, ne, t0, Operand(zero_reg));
|
| + // Check if the object is a JS array or not.
|
| + __ lbu(t3, FieldMemOperand(t3, Map::kInstanceTypeOffset));
|
| +
|
| + __ Branch(&array, eq, t3, Operand(JS_ARRAY_TYPE));
|
| + // Check that the object is some kind of JS object.
|
| + __ Branch(&slow, lt, t3, Operand(FIRST_JS_OBJECT_TYPE));
|
| +
|
| + // Object case: Check key against length in the elements array.
|
| + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
| + // Check that the object is in fast mode and writable.
|
| + __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset));
|
| + __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex);
|
| + __ Branch(&slow, ne, t3, Operand(t0));
|
| + // Check array bounds. Both the key and the length of FixedArray are smis.
|
| + __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
| + __ Branch(&fast, lo, key, Operand(t0));
|
| + // Fall thru to slow if un-tagged index >= length.
|
| +
|
| + // Slow case, handle jump to runtime.
|
| + __ bind(&slow);
|
| +
|
| + // Entry registers are intact.
|
| + // a0: value.
|
| + // a1: key.
|
| + // a2: receiver.
|
| +
|
| + GenerateRuntimeSetProperty(masm, strict_mode);
|
| +
|
| + // Extra capacity case: Check if there is extra capacity to
|
| + // perform the store and update the length. Used for adding one
|
| + // element to the array by writing to array[array.length].
|
| +
|
| + __ bind(&extra);
|
| + // Only support writing to array[array.length].
|
| + __ Branch(&slow, ne, key, Operand(t0));
|
| + // Check for room in the elements backing store.
|
| + // Both the key and the length of FixedArray are smis.
|
| + __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
| + __ Branch(&slow, hs, key, Operand(t0));
|
| + // Calculate key + 1 as smi.
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ Addu(t3, key, Operand(Smi::FromInt(1)));
|
| + __ sw(t3, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ Branch(&fast);
|
| +
|
| +
|
| + // Array case: Get the length and the elements array from the JS
|
| + // array. Check that the array is in fast mode (and writable); if it
|
| + // is the length is always a smi.
|
| +
|
| + __ bind(&array);
|
| + __ lw(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
|
| + __ lw(t3, FieldMemOperand(elements, HeapObject::kMapOffset));
|
| + __ LoadRoot(t0, Heap::kFixedArrayMapRootIndex);
|
| + __ Branch(&slow, ne, t3, Operand(t0));
|
| +
|
| + // Check the key against the length in the array.
|
| + __ lw(t0, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ Branch(&extra, hs, key, Operand(t0));
|
| + // Fall through to fast case.
|
| +
|
| + __ bind(&fast);
|
| + // Fast case, store the value to the elements backing store.
|
| + __ Addu(t4, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
| + __ sll(t1, key, kPointerSizeLog2 - kSmiTagSize);
|
| + __ Addu(t4, t4, Operand(t1));
|
| + __ sw(value, MemOperand(t4));
|
| + // Skip write barrier if the written value is a smi.
|
| + __ JumpIfSmi(value, &exit);
|
| +
|
| + // Update write barrier for the elements array address.
|
| + __ Subu(t3, t4, Operand(elements));
|
| +
|
| + __ RecordWrite(elements, Operand(t3), t4, t5);
|
| + __ bind(&exit);
|
| +
|
| + __ mov(v0, a0); // Return the value written.
|
| + __ Ret();
|
| }
|
|
|
|
|
| void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- ra : return address
|
| + // -- a0 : key
|
| + // -- a1 : receiver
|
| + // -----------------------------------
|
| + Label slow;
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(a1, &slow);
|
| +
|
| + // Check that the key is an array index, that is Uint32.
|
| + __ And(t0, a0, Operand(kSmiTagMask | kSmiSignMask));
|
| + __ Branch(&slow, ne, t0, Operand(zero_reg));
|
| +
|
| + // Get the map of the receiver.
|
| + __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
|
| +
|
| + // Check that it has indexed interceptor and access checks
|
| + // are not enabled for this object.
|
| + __ lbu(a3, FieldMemOperand(a2, Map::kBitFieldOffset));
|
| + __ And(a3, a3, Operand(kSlowCaseBitFieldMask));
|
| + __ Branch(&slow, ne, a3, Operand(1 << Map::kHasIndexedInterceptor));
|
| + // Everything is fine, call runtime.
|
| + __ Push(a1, a0); // Receiver, key.
|
| +
|
| + // Perform tail call to the entry.
|
| + __ TailCallExternalReference(ExternalReference(
|
| + IC_Utility(kKeyedLoadPropertyWithInterceptor), masm->isolate()), 2, 1);
|
| +
|
| + __ bind(&slow);
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ---------- S t a t e --------------
|
| + // -- a0 : value
|
| + // -- a1 : key
|
| + // -- a2 : receiver
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Push receiver, key and value for runtime call.
|
| + // We can't use MultiPush as the order of the registers is important.
|
| + __ Push(a2, a1, a0);
|
| +
|
| + ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss),
|
| + masm->isolate());
|
| + __ TailCallExternalReference(ref, 3, 1);
|
| }
|
|
|
|
|
| void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
|
| StrictModeFlag strict_mode) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a0 : value
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + // Get the receiver from the stack and probe the stub cache.
|
| + Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
|
| + NOT_IN_LOOP,
|
| + MONOMORPHIC,
|
| + strict_mode);
|
| + Isolate::Current()->stub_cache()->GenerateProbe(
|
| + masm, flags, a1, a2, a3, t0, t1);
|
| +
|
| + // Cache miss: Jump to runtime.
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void StoreIC::GenerateMiss(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a0 : value
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + __ Push(a1, a2, a0);
|
| + // Perform tail call to the entry.
|
| + ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss),
|
| + masm->isolate());
|
| + __ TailCallExternalReference(ref, 3, 1);
|
| }
|
|
|
|
|
| void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a0 : value
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| + //
|
| + // This accepts as a receiver anything JSObject::SetElementsLength accepts
|
| + // (currently anything except for external and pixel arrays which means
|
| + // anything with elements of FixedArray type.), but currently is restricted
|
| + // to JSArray.
|
| + // Value must be a number, but only smis are accepted as the most common case.
|
| +
|
| + Label miss;
|
| +
|
| + Register receiver = a1;
|
| + Register value = a0;
|
| + Register scratch = a3;
|
| +
|
| + // Check that the receiver isn't a smi.
|
| + __ JumpIfSmi(receiver, &miss);
|
| +
|
| + // Check that the object is a JS array.
|
| + __ GetObjectType(receiver, scratch, scratch);
|
| + __ Branch(&miss, ne, scratch, Operand(JS_ARRAY_TYPE));
|
| +
|
| + // Check that elements are FixedArray.
|
| + // We rely on StoreIC_ArrayLength below to deal with all types of
|
| + // fast elements (including COW).
|
| + __ lw(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
|
| + __ GetObjectType(scratch, scratch, scratch);
|
| + __ Branch(&miss, ne, scratch, Operand(FIXED_ARRAY_TYPE));
|
| +
|
| + // Check that value is a smi.
|
| + __ JumpIfNotSmi(value, &miss);
|
| +
|
| + // Prepare tail call to StoreIC_ArrayLength.
|
| + __ Push(receiver, value);
|
| +
|
| + ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength),
|
| + masm->isolate());
|
| + __ TailCallExternalReference(ref, 2, 1);
|
| +
|
| + __ bind(&miss);
|
| +
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void StoreIC::GenerateNormal(MacroAssembler* masm) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a0 : value
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| + Label miss;
|
| +
|
| + GenerateStringDictionaryReceiverCheck(masm, a1, a3, t0, t1, &miss);
|
| +
|
| + GenerateDictionaryStore(masm, &miss, a3, a2, a0, t0, t1);
|
| + Counters* counters = masm->isolate()->counters();
|
| + __ IncrementCounter(counters->store_normal_hit(), 1, t0, t1);
|
| + __ Ret();
|
| +
|
| + __ bind(&miss);
|
| + __ IncrementCounter(counters->store_normal_miss(), 1, t0, t1);
|
| + GenerateMiss(masm);
|
| }
|
|
|
|
|
| void StoreIC::GenerateGlobalProxy(MacroAssembler* masm,
|
| StrictModeFlag strict_mode) {
|
| - UNIMPLEMENTED_MIPS();
|
| + // ----------- S t a t e -------------
|
| + // -- a0 : value
|
| + // -- a1 : receiver
|
| + // -- a2 : name
|
| + // -- ra : return address
|
| + // -----------------------------------
|
| +
|
| + __ Push(a1, a2, a0);
|
| +
|
| + __ li(a1, Operand(Smi::FromInt(NONE))); // PropertyAttributes.
|
| + __ li(a0, Operand(Smi::FromInt(strict_mode)));
|
| + __ Push(a1, a0);
|
| +
|
| + // Do tail-call to runtime routine.
|
| + __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
|
| }
|
|
|
|
|
| @@ -191,18 +1419,119 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm,
|
|
|
|
|
| Condition CompareIC::ComputeCondition(Token::Value op) {
|
| - UNIMPLEMENTED_MIPS();
|
| - return kNoCondition;
|
| + switch (op) {
|
| + case Token::EQ_STRICT:
|
| + case Token::EQ:
|
| + return eq;
|
| + case Token::LT:
|
| + return lt;
|
| + case Token::GT:
|
| + // Reverse left and right operands to obtain ECMA-262 conversion order.
|
| + return lt;
|
| + case Token::LTE:
|
| + // Reverse left and right operands to obtain ECMA-262 conversion order.
|
| + return ge;
|
| + case Token::GTE:
|
| + return ge;
|
| + default:
|
| + UNREACHABLE();
|
| + return kNoCondition;
|
| + }
|
| }
|
|
|
|
|
| void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
|
| - UNIMPLEMENTED_MIPS();
|
| + HandleScope scope;
|
| + Handle<Code> rewritten;
|
| + State previous_state = GetState();
|
| + State state = TargetState(previous_state, false, x, y);
|
| + if (state == GENERIC) {
|
| + CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, a1, a0);
|
| + rewritten = stub.GetCode();
|
| + } else {
|
| + ICCompareStub stub(op_, state);
|
| + rewritten = stub.GetCode();
|
| + }
|
| + set_target(*rewritten);
|
| +
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_ic) {
|
| + PrintF("[CompareIC (%s->%s)#%s]\n",
|
| + GetStateName(previous_state),
|
| + GetStateName(state),
|
| + Token::Name(op_));
|
| + }
|
| +#endif
|
| +
|
| + // Activate inlined smi code.
|
| + if (previous_state == UNINITIALIZED) {
|
| + PatchInlinedSmiCode(address());
|
| + }
|
| }
|
|
|
|
|
| void PatchInlinedSmiCode(Address address) {
|
| - // Currently there is no smi inlining in the MIPS full code generator.
|
| + Address andi_instruction_address =
|
| + address + Assembler::kCallTargetAddressOffset;
|
| +
|
| + // If the instruction following the call is not a andi at, rx, #yyy, nothing
|
| + // was inlined.
|
| + Instr instr = Assembler::instr_at(andi_instruction_address);
|
| + if (!Assembler::IsAndImmediate(instr)) {
|
| + return;
|
| + }
|
| +
|
| + // The delta to the start of the map check instruction and the
|
| + // condition code uses at the patched jump.
|
| + int delta = Assembler::GetImmediate16(instr);
|
| + delta += Assembler::GetRs(instr) * kImm16Mask;
|
| + // If the delta is 0 the instruction is andi at, zero_reg, #0 which also
|
| + // signals that nothing was inlined.
|
| + if (delta == 0) {
|
| + return;
|
| + }
|
| +
|
| +#ifdef DEBUG
|
| + if (FLAG_trace_ic) {
|
| + PrintF("[ patching ic at %p, andi=%p, delta=%d\n",
|
| + address, andi_instruction_address, delta);
|
| + }
|
| +#endif
|
| +
|
| + Address patch_address =
|
| + andi_instruction_address - delta * Instruction::kInstrSize;
|
| + Instr instr_at_patch = Assembler::instr_at(patch_address);
|
| + Instr branch_instr =
|
| + Assembler::instr_at(patch_address + Instruction::kInstrSize);
|
| + ASSERT(Assembler::IsAndImmediate(instr_at_patch));
|
| + ASSERT_EQ(0, Assembler::GetImmediate16(instr_at_patch));
|
| + ASSERT(Assembler::IsBranch(branch_instr));
|
| + if (Assembler::IsBeq(branch_instr)) {
|
| + // This is patching a "jump if not smi" site to be active.
|
| + // Changing:
|
| + // andi at, rx, 0
|
| + // Branch <target>, eq, at, Operand(zero_reg)
|
| + // to:
|
| + // andi at, rx, #kSmiTagMask
|
| + // Branch <target>, ne, at, Operand(zero_reg)
|
| + CodePatcher patcher(patch_address, 2);
|
| + Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
|
| + patcher.masm()->andi(at, reg, kSmiTagMask);
|
| + patcher.ChangeBranchCondition(ne);
|
| + } else {
|
| + ASSERT(Assembler::IsBne(branch_instr));
|
| + // This is patching a "jump if smi" site to be active.
|
| + // Changing:
|
| + // andi at, rx, 0
|
| + // Branch <target>, ne, at, Operand(zero_reg)
|
| + // to:
|
| + // andi at, rx, #kSmiTagMask
|
| + // Branch <target>, eq, at, Operand(zero_reg)
|
| + CodePatcher patcher(patch_address, 2);
|
| + Register reg = Register::from_code(Assembler::GetRs(instr_at_patch));
|
| + patcher.masm()->andi(at, reg, kSmiTagMask);
|
| + patcher.ChangeBranchCondition(eq);
|
| + }
|
| }
|
|
|
|
|
|
|