Index: src/mips/macro-assembler-mips.cc |
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc |
index d7732c6a381b3ddbc61534c51de4f4764f43c4aa..6e7c28caebc0381dc42b148bf736ecf84feba9f6 100644 |
--- a/src/mips/macro-assembler-mips.cc |
+++ b/src/mips/macro-assembler-mips.cc |
@@ -81,39 +81,6 @@ void MacroAssembler::StoreRoot(Register source, |
} |
-void MacroAssembler::RecordWriteHelper(Register object, |
- Register address, |
- Register scratch) { |
- if (emit_debug_code()) { |
- // Check that the object is not in new space. |
- Label not_in_new_space; |
- InNewSpace(object, scratch, ne, ¬_in_new_space); |
- Abort("new-space object passed to RecordWriteHelper"); |
- bind(¬_in_new_space); |
- } |
- |
- // Calculate page address: Clear bits from 0 to kPageSizeBits. |
- if (mips32r2) { |
- Ins(object, zero_reg, 0, kPageSizeBits); |
- } else { |
- // The Ins macro is slow on r1, so use shifts instead. |
- srl(object, object, kPageSizeBits); |
- sll(object, object, kPageSizeBits); |
- } |
- |
- // Calculate region number. |
- Ext(address, address, Page::kRegionSizeLog2, |
- kPageSizeBits - Page::kRegionSizeLog2); |
- |
- // Mark region dirty. |
- lw(scratch, MemOperand(object, Page::kDirtyFlagOffset)); |
- li(at, Operand(1)); |
- sllv(at, at, address); |
- or_(scratch, scratch, at); |
- sw(scratch, MemOperand(object, Page::kDirtyFlagOffset)); |
-} |
- |
- |
// Push and pop all registers that can hold pointers. |
void MacroAssembler::PushSafepointRegisters() { |
// Safepoints expect a block of kNumSafepointRegisters values on the |
@@ -204,38 +171,53 @@ void MacroAssembler::InNewSpace(Register object, |
} |
-// Will clobber 4 registers: object, scratch0, scratch1, at. The |
-// register 'object' contains a heap object pointer. The heap object |
-// tag is shifted away. |
-void MacroAssembler::RecordWrite(Register object, |
- Operand offset, |
- Register scratch0, |
- Register scratch1) { |
- // The compiled code assumes that record write doesn't change the |
- // context register, so we check that none of the clobbered |
- // registers are cp. |
- ASSERT(!object.is(cp) && !scratch0.is(cp) && !scratch1.is(cp)); |
- |
+void MacroAssembler::RecordWriteField( |
+ Register object, |
+ int offset, |
+ Register value, |
+ Register dst, |
+ RAStatus ra_status, |
+ SaveFPRegsMode save_fp, |
+ RememberedSetAction remembered_set_action, |
+ SmiCheck smi_check) { |
+ ASSERT(!AreAliased(value, dst, t8, object)); |
+ // First, check if a write barrier is even needed. The tests below |
+ // catch stores of Smis. |
Label done; |
- // First, test that the object is not in the new space. We cannot set |
- // region marks for new space pages. |
- InNewSpace(object, scratch0, eq, &done); |
+ // Skip barrier if writing a smi. |
+ if (smi_check == INLINE_SMI_CHECK) { |
+ JumpIfSmi(value, &done); |
+ } |
- // Add offset into the object. |
- Addu(scratch0, object, offset); |
+ // Although the object register is tagged, the offset is relative to the start |
+ // of the object, so so offset must be a multiple of kPointerSize. |
+ ASSERT(IsAligned(offset, kPointerSize)); |
- // Record the actual write. |
- RecordWriteHelper(object, scratch0, scratch1); |
+ Addu(dst, object, Operand(offset - kHeapObjectTag)); |
+ if (emit_debug_code()) { |
+ Label ok; |
+ And(t8, dst, Operand((1 << kPointerSizeLog2) - 1)); |
+ Branch(&ok, eq, t8, Operand(zero_reg)); |
+ stop("Unaligned cell in write barrier"); |
+ bind(&ok); |
+ } |
+ |
+ RecordWrite(object, |
+ dst, |
+ value, |
+ ra_status, |
+ save_fp, |
+ remembered_set_action, |
+ OMIT_SMI_CHECK); |
bind(&done); |
- // Clobber all input registers when running with the debug-code flag |
+ // Clobber clobbered input registers when running with the debug-code flag |
// turned on to provoke errors. |
if (emit_debug_code()) { |
- li(object, Operand(BitCast<int32_t>(kZapValue))); |
- li(scratch0, Operand(BitCast<int32_t>(kZapValue))); |
- li(scratch1, Operand(BitCast<int32_t>(kZapValue))); |
+ li(value, Operand(BitCast<int32_t>(kZapValue + 4))); |
+ li(dst, Operand(BitCast<int32_t>(kZapValue + 8))); |
} |
} |
@@ -245,29 +227,90 @@ void MacroAssembler::RecordWrite(Register object, |
// tag is shifted away. |
void MacroAssembler::RecordWrite(Register object, |
Register address, |
- Register scratch) { |
+ Register value, |
+ RAStatus ra_status, |
+ SaveFPRegsMode fp_mode, |
+ RememberedSetAction remembered_set_action, |
+ SmiCheck smi_check) { |
+ ASSERT(!AreAliased(object, address, value, t8)); |
+ ASSERT(!AreAliased(object, address, value, t9)); |
// The compiled code assumes that record write doesn't change the |
// context register, so we check that none of the clobbered |
// registers are cp. |
- ASSERT(!object.is(cp) && !address.is(cp) && !scratch.is(cp)); |
+ ASSERT(!address.is(cp) && !value.is(cp)); |
Label done; |
- // First, test that the object is not in the new space. We cannot set |
- // region marks for new space pages. |
- InNewSpace(object, scratch, eq, &done); |
+ if (smi_check == INLINE_SMI_CHECK) { |
+ ASSERT_EQ(0, kSmiTag); |
+ And(t8, value, Operand(kSmiTagMask)); |
+ Branch(&done, eq, t8, Operand(zero_reg)); |
+ } |
+ |
+ CheckPageFlag(value, |
+ value, // Used as scratch. |
+ MemoryChunk::kPointersToHereAreInterestingMask, |
+ eq, |
+ &done); |
+ CheckPageFlag(object, |
+ value, // Used as scratch. |
+ MemoryChunk::kPointersFromHereAreInterestingMask, |
+ eq, |
+ &done); |
// Record the actual write. |
- RecordWriteHelper(object, address, scratch); |
+ if (ra_status == kRAHasNotBeenSaved) { |
+ push(ra); |
+ } |
+ RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); |
+ CallStub(&stub); |
+ if (ra_status == kRAHasNotBeenSaved) { |
+ pop(ra); |
+ } |
bind(&done); |
- // Clobber all input registers when running with the debug-code flag |
+ // Clobber clobbered registers when running with the debug-code flag |
// turned on to provoke errors. |
if (emit_debug_code()) { |
- li(object, Operand(BitCast<int32_t>(kZapValue))); |
- li(address, Operand(BitCast<int32_t>(kZapValue))); |
- li(scratch, Operand(BitCast<int32_t>(kZapValue))); |
+ li(address, Operand(BitCast<int32_t>(kZapValue + 12))); |
+ li(value, Operand(BitCast<int32_t>(kZapValue + 16))); |
+ } |
+} |
+ |
+ |
+void MacroAssembler::RememberedSetHelper(Register address, |
+ Register scratch, |
+ SaveFPRegsMode fp_mode, |
+ RememberedSetFinalAction and_then) { |
+ Label done; |
+ // Load store buffer top. |
+ ExternalReference store_buffer = |
+ ExternalReference::store_buffer_top(isolate()); |
+ li(t8, Operand(store_buffer)); |
+ lw(scratch, MemOperand(t8)); |
+ // Store pointer to buffer and increment buffer top. |
+ sw(address, MemOperand(scratch)); |
+ Addu(scratch, scratch, kPointerSize); |
+ // Write back new top of buffer. |
+ sw(scratch, MemOperand(t8)); |
+ // Call stub on end of buffer. |
+ // Check for end of buffer. |
+ And(t8, scratch, Operand(StoreBuffer::kStoreBufferOverflowBit)); |
+ if (and_then == kFallThroughAtEnd) { |
+ Branch(&done, eq, t8, Operand(zero_reg)); |
+ } else { |
+ ASSERT(and_then == kReturnAtEnd); |
+ Ret(ne, t8, Operand(zero_reg)); |
+ } |
+ push(ra); |
+ StoreBufferOverflowStub store_buffer_overflow = |
+ StoreBufferOverflowStub(fp_mode); |
+ CallStub(&store_buffer_overflow); |
+ pop(ra); |
+ bind(&done); |
+ if (and_then == kReturnAtEnd) { |
+ Ret(); |
} |
} |
@@ -3879,8 +3922,7 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { |
const Runtime::Function* function = Runtime::FunctionForId(id); |
li(a0, Operand(function->nargs)); |
li(a1, Operand(ExternalReference(function, isolate()))); |
- CEntryStub stub(1); |
- stub.SaveDoubles(); |
+ CEntryStub stub(1, kSaveFPRegs); |
CallStub(&stub); |
} |
@@ -4652,6 +4694,204 @@ void MacroAssembler::PatchRelocatedValue(Register li_location, |
} |
+void MacroAssembler::CheckPageFlag( |
+ Register object, |
+ Register scratch, |
+ int mask, |
+ Condition cc, |
+ Label* condition_met) { |
+ And(scratch, object, Operand(~Page::kPageAlignmentMask)); |
+ lw(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); |
+ And(scratch, scratch, Operand(mask)); |
+ Branch(condition_met, cc, scratch, Operand(zero_reg)); |
+} |
+ |
+ |
+void MacroAssembler::JumpIfBlack(Register object, |
+ Register scratch0, |
+ Register scratch1, |
+ Label* on_black) { |
+ HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern. |
+ ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); |
+} |
+ |
+ |
+void MacroAssembler::HasColor(Register object, |
+ Register bitmap_scratch, |
+ Register mask_scratch, |
+ Label* has_color, |
+ int first_bit, |
+ int second_bit) { |
+ ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, t8)); |
+ ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, t9)); |
+ |
+ GetMarkBits(object, bitmap_scratch, mask_scratch); |
+ |
+ Label other_color, word_boundary; |
+ lw(t9, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); |
+ And(t8, t9, Operand(mask_scratch)); |
+ Branch(&other_color, first_bit == 1 ? eq : ne, t8, Operand(zero_reg)); |
+ // Shift left 1 by adding. |
+ Addu(mask_scratch, mask_scratch, Operand(mask_scratch)); |
+ Branch(&word_boundary, eq, mask_scratch, Operand(zero_reg)); |
+ And(t8, t9, Operand(mask_scratch)); |
+ Branch(has_color, second_bit == 1 ? ne : eq, t8, Operand(zero_reg)); |
+ jmp(&other_color); |
+ |
+ bind(&word_boundary); |
+ lw(t9, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize)); |
+ And(t9, t9, Operand(1)); |
+ Branch(has_color, second_bit == 1 ? ne : eq, t9, Operand(zero_reg)); |
+ bind(&other_color); |
+} |
+ |
+ |
+// Detect some, but not all, common pointer-free objects. This is used by the |
+// incremental write barrier which doesn't care about oddballs (they are always |
+// marked black immediately so this code is not hit). |
+void MacroAssembler::JumpIfDataObject(Register value, |
+ Register scratch, |
+ Label* not_data_object) { |
+ ASSERT(!AreAliased(value, scratch, t8, no_reg)); |
+ Label is_data_object; |
+ lw(scratch, FieldMemOperand(value, HeapObject::kMapOffset)); |
+ LoadRoot(t8, Heap::kHeapNumberMapRootIndex); |
+ Branch(&is_data_object, eq, t8, Operand(scratch)); |
+ ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); |
+ ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); |
+ // If it's a string and it's not a cons string then it's an object containing |
+ // no GC pointers. |
+ lbu(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); |
+ And(t8, scratch, Operand(kIsIndirectStringMask | kIsNotStringMask)); |
+ Branch(not_data_object, ne, t8, Operand(zero_reg)); |
+ bind(&is_data_object); |
+} |
+ |
+ |
+void MacroAssembler::GetMarkBits(Register addr_reg, |
+ Register bitmap_reg, |
+ Register mask_reg) { |
+ ASSERT(!AreAliased(addr_reg, bitmap_reg, mask_reg, no_reg)); |
+ And(bitmap_reg, addr_reg, Operand(~Page::kPageAlignmentMask)); |
+ Ext(mask_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); |
+ const int kLowBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; |
+ Ext(t8, addr_reg, kLowBits, kPageSizeBits - kLowBits); |
+ sll(t8, t8, kPointerSizeLog2); |
+ Addu(bitmap_reg, bitmap_reg, t8); |
+ li(t8, Operand(1)); |
+ sllv(mask_reg, t8, mask_reg); |
+} |
+ |
+ |
+void MacroAssembler::EnsureNotWhite( |
+ Register value, |
+ Register bitmap_scratch, |
+ Register mask_scratch, |
+ Register load_scratch, |
+ Label* value_is_white_and_not_data) { |
+ ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, t8)); |
+ GetMarkBits(value, bitmap_scratch, mask_scratch); |
+ |
+ // If the value is black or grey we don't need to do anything. |
+ ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); |
+ ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); |
+ ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); |
+ ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); |
+ |
+ Label done; |
+ |
+ // Since both black and grey have a 1 in the first position and white does |
+ // not have a 1 there we only need to check one bit. |
+ lw(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); |
+ And(t8, mask_scratch, load_scratch); |
+ Branch(&done, ne, t8, Operand(zero_reg)); |
+ |
+ if (FLAG_debug_code) { |
+ // Check for impossible bit pattern. |
+ Label ok; |
+ // sll may overflow, making the check conservative. |
+ sll(t8, mask_scratch, 1); |
+ And(t8, load_scratch, t8); |
+ Branch(&ok, eq, t8, Operand(zero_reg)); |
+ stop("Impossible marking bit pattern"); |
+ bind(&ok); |
+ } |
+ |
+ // Value is white. We check whether it is data that doesn't need scanning. |
+ // Currently only checks for HeapNumber and non-cons strings. |
+ Register map = load_scratch; // Holds map while checking type. |
+ Register length = load_scratch; // Holds length of object after testing type. |
+ Label is_data_object; |
+ |
+ // Check for heap-number |
+ lw(map, FieldMemOperand(value, HeapObject::kMapOffset)); |
+ LoadRoot(t8, Heap::kHeapNumberMapRootIndex); |
+ { |
+ Label skip; |
+ Branch(&skip, ne, t8, Operand(map)); |
+ li(length, HeapNumber::kSize); |
+ Branch(&is_data_object); |
+ bind(&skip); |
+ } |
+ |
+ // Check for strings. |
+ ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); |
+ ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); |
+ // If it's a string and it's not a cons string then it's an object containing |
+ // no GC pointers. |
+ Register instance_type = load_scratch; |
+ lbu(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); |
+ And(t8, instance_type, Operand(kIsIndirectStringMask | kIsNotStringMask)); |
+ Branch(value_is_white_and_not_data, ne, t8, Operand(zero_reg)); |
+ // It's a non-indirect (non-cons and non-slice) string. |
+ // If it's external, the length is just ExternalString::kSize. |
+ // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). |
+ // External strings are the only ones with the kExternalStringTag bit |
+ // set. |
+ ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); |
+ ASSERT_EQ(0, kConsStringTag & kExternalStringTag); |
+ And(t8, instance_type, Operand(kExternalStringTag)); |
+ { |
+ Label skip; |
+ Branch(&skip, eq, t8, Operand(zero_reg)); |
+ li(length, ExternalString::kSize); |
+ Branch(&is_data_object); |
+ bind(&skip); |
+ } |
+ |
+ // Sequential string, either ASCII or UC16. |
+ // For ASCII (char-size of 1) we shift the smi tag away to get the length. |
+ // For UC16 (char-size of 2) we just leave the smi tag in place, thereby |
+ // getting the length multiplied by 2. |
+ ASSERT(kAsciiStringTag == 4 && kStringEncodingMask == 4); |
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
+ lw(t9, FieldMemOperand(value, String::kLengthOffset)); |
+ And(t8, instance_type, Operand(kStringEncodingMask)); |
+ { |
+ Label skip; |
+ Branch(&skip, eq, t8, Operand(zero_reg)); |
+ srl(t9, t9, 1); |
+ bind(&skip); |
+ } |
+ Addu(length, t9, Operand(SeqString::kHeaderSize + kObjectAlignmentMask)); |
+ And(length, length, Operand(~kObjectAlignmentMask)); |
+ |
+ bind(&is_data_object); |
+ // Value is a data object, and it is white. Mark it black. Since we know |
+ // that the object is white we can make it black by flipping one bit. |
+ lw(t8, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); |
+ Or(t8, t8, Operand(mask_scratch)); |
+ sw(t8, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); |
+ |
+ And(bitmap_scratch, bitmap_scratch, Operand(~Page::kPageAlignmentMask)); |
+ lw(t8, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); |
+ Addu(t8, t8, Operand(length)); |
+ sw(t8, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); |
+ |
+ bind(&done); |
+} |
+ |
+ |
void MacroAssembler::LoadInstanceDescriptors(Register map, |
Register descriptors) { |
lw(descriptors, |
@@ -4706,6 +4946,17 @@ void MacroAssembler::ClampDoubleToUint8(Register result_reg, |
} |
+bool AreAliased(Register r1, Register r2, Register r3, Register r4) { |
+ if (r1.is(r2)) return true; |
+ if (r1.is(r3)) return true; |
+ if (r1.is(r4)) return true; |
+ if (r2.is(r3)) return true; |
+ if (r2.is(r4)) return true; |
+ if (r3.is(r4)) return true; |
+ return false; |
+} |
+ |
+ |
CodePatcher::CodePatcher(byte* address, int instructions) |
: address_(address), |
instructions_(instructions), |