MIPS: port Merge experimental/gc branch to the bleeding_edge.
Simplified based on Michael's change Refactor how embedded pointers are visited. (9597) Ported r9328 (bdc13b7) BUG= TEST= Review URL: http://codereview.chromium.org/8106002 Patch from Paul Lind <pling44@gmail.com>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9600 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
6112eb9bda
commit
4f7d11f963
@ -78,7 +78,6 @@ bool Operand::is_reg() const {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RelocInfo.
|
||||
|
||||
@ -120,6 +119,11 @@ int RelocInfo::target_address_size() {
|
||||
void RelocInfo::set_target_address(Address target) {
|
||||
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
|
||||
Assembler::set_target_address_at(pc_, target);
|
||||
if (host() != NULL && IsCodeTarget(rmode_)) {
|
||||
Object* target_code = Code::GetCodeFromTargetAddress(target);
|
||||
host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
|
||||
host(), this, HeapObject::cast(target_code));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -149,6 +153,10 @@ Object** RelocInfo::target_object_address() {
|
||||
void RelocInfo::set_target_object(Object* target) {
|
||||
ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
|
||||
Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target));
|
||||
if (host() != NULL && target->IsHeapObject()) {
|
||||
host()->GetHeap()->incremental_marking()->RecordWrite(
|
||||
host(), &Memory::Object_at(pc_), HeapObject::cast(target));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -180,6 +188,12 @@ void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell) {
|
||||
ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL);
|
||||
Address address = cell->address() + JSGlobalPropertyCell::kValueOffset;
|
||||
Memory::Address_at(pc_) = address;
|
||||
if (host() != NULL) {
|
||||
// TODO(1550) We are passing NULL as a slot because cell can never be on
|
||||
// evacuation candidate.
|
||||
host()->GetHeap()->incremental_marking()->RecordWrite(
|
||||
host(), NULL, cell);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -200,6 +214,11 @@ void RelocInfo::set_call_address(Address target) {
|
||||
// debug-mips.cc BreakLocationIterator::SetDebugBreakAtReturn(), or
|
||||
// debug break slot per BreakLocationIterator::SetDebugBreakAtSlot().
|
||||
Assembler::set_target_address_at(pc_, target);
|
||||
if (host() != NULL) {
|
||||
Object* target_code = Code::GetCodeFromTargetAddress(target);
|
||||
host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
|
||||
host(), this, HeapObject::cast(target_code));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -242,12 +261,7 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
|
||||
void RelocInfo::Visit(ObjectVisitor* visitor) {
|
||||
RelocInfo::Mode mode = rmode();
|
||||
if (mode == RelocInfo::EMBEDDED_OBJECT) {
|
||||
Object** p = target_object_address();
|
||||
Object* orig = *p;
|
||||
visitor->VisitEmbeddedPointer(host(), p);
|
||||
if (*p != orig) {
|
||||
set_target_object(*p);
|
||||
}
|
||||
visitor->VisitEmbeddedPointer(this);
|
||||
} else if (RelocInfo::IsCodeTarget(mode)) {
|
||||
visitor->VisitCodeTarget(this);
|
||||
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
|
||||
@ -273,7 +287,7 @@ template<typename StaticVisitor>
|
||||
void RelocInfo::Visit(Heap* heap) {
|
||||
RelocInfo::Mode mode = rmode();
|
||||
if (mode == RelocInfo::EMBEDDED_OBJECT) {
|
||||
StaticVisitor::VisitEmbeddedPointer(heap, host(), target_object_address());
|
||||
StaticVisitor::VisitEmbeddedPointer(heap, this);
|
||||
} else if (RelocInfo::IsCodeTarget(mode)) {
|
||||
StaticVisitor::VisitCodeTarget(heap, this);
|
||||
} else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
|
||||
|
@ -74,7 +74,9 @@ static uint64_t CpuFeaturesImpliedByCompiler() {
|
||||
|
||||
|
||||
void CpuFeatures::Probe() {
|
||||
ASSERT(!initialized_);
|
||||
unsigned standard_features = (OS::CpuFeaturesImpliedByPlatform() |
|
||||
CpuFeaturesImpliedByCompiler());
|
||||
ASSERT(supported_ == 0 || supported_ == standard_features);
|
||||
#ifdef DEBUG
|
||||
initialized_ = true;
|
||||
#endif
|
||||
@ -82,8 +84,7 @@ void CpuFeatures::Probe() {
|
||||
// Get the features implied by the OS and the compiler settings. This is the
|
||||
// minimal set of features which is also allowed for generated code in the
|
||||
// snapshot.
|
||||
supported_ |= OS::CpuFeaturesImpliedByPlatform();
|
||||
supported_ |= CpuFeaturesImpliedByCompiler();
|
||||
supported_ |= standard_features;
|
||||
|
||||
if (Serializer::enabled()) {
|
||||
// No probing for features if we might serialize (generate snapshot).
|
||||
@ -2018,7 +2019,8 @@ void Assembler::dd(uint32_t data) {
|
||||
|
||||
|
||||
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
|
||||
RelocInfo rinfo(pc_, rmode, data); // We do not try to reuse pool constants.
|
||||
// We do not try to reuse pool constants.
|
||||
RelocInfo rinfo(pc_, rmode, data, NULL);
|
||||
if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::DEBUG_BREAK_SLOT) {
|
||||
// Adjust code for new modes.
|
||||
ASSERT(RelocInfo::IsDebugBreakSlot(rmode)
|
||||
@ -2041,7 +2043,7 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
|
||||
}
|
||||
ASSERT(buffer_space() >= kMaxRelocSize); // Too late to grow buffer here.
|
||||
if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
|
||||
RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId());
|
||||
RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId(), NULL);
|
||||
ClearRecordedAstId();
|
||||
reloc_info_writer.Write(&reloc_info_with_ast_id);
|
||||
} else {
|
||||
|
@ -886,6 +886,35 @@ void FloatingPointHelper::CallCCodeForDoubleOperation(
|
||||
}
|
||||
|
||||
|
||||
bool WriteInt32ToHeapNumberStub::CompilingCallsToThisStubIsGCSafe() {
|
||||
// These variants are compiled ahead of time. See next method.
|
||||
if (the_int_.is(a1) &&
|
||||
the_heap_number_.is(v0) &&
|
||||
scratch_.is(a2) &&
|
||||
sign_.is(a3)) {
|
||||
return true;
|
||||
}
|
||||
if (the_int_.is(a2) &&
|
||||
the_heap_number_.is(v0) &&
|
||||
scratch_.is(a3) &&
|
||||
sign_.is(a0)) {
|
||||
return true;
|
||||
}
|
||||
// Other register combinations are generated as and when they are needed,
|
||||
// so it is unsafe to call them from stubs (we can't generate a stub while
|
||||
// we are generating a stub).
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime() {
|
||||
WriteInt32ToHeapNumberStub stub1(a1, v0, a2, a3);
|
||||
WriteInt32ToHeapNumberStub stub2(a2, v0, a3, a0);
|
||||
Handle<Code> code1 = stub1.GetCode();
|
||||
Handle<Code> code2 = stub2.GetCode();
|
||||
}
|
||||
|
||||
|
||||
// See comment for class, this does NOT work for int32's that are in Smi range.
|
||||
void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
|
||||
Label max_negative_int;
|
||||
@ -1801,6 +1830,35 @@ void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
|
||||
// We don't allow a GC during a store buffer overflow so there is no need to
|
||||
// store the registers in any particular way, but we do have to store and
|
||||
// restore them.
|
||||
__ MultiPush(kJSCallerSaved | ra.bit());
|
||||
if (save_doubles_ == kSaveFPRegs) {
|
||||
CpuFeatures::Scope scope(FPU);
|
||||
__ MultiPushFPU(kCallerSavedFPU);
|
||||
}
|
||||
const int argument_count = 1;
|
||||
const int fp_argument_count = 0;
|
||||
const Register scratch = a1;
|
||||
|
||||
AllowExternalCallThatCantCauseGC scope(masm);
|
||||
__ PrepareCallCFunction(argument_count, fp_argument_count, scratch);
|
||||
__ li(a0, Operand(ExternalReference::isolate_address()));
|
||||
__ CallCFunction(
|
||||
ExternalReference::store_buffer_overflow_function(masm->isolate()),
|
||||
argument_count);
|
||||
if (save_doubles_ == kSaveFPRegs) {
|
||||
CpuFeatures::Scope scope(FPU);
|
||||
__ MultiPopFPU(kCallerSavedFPU);
|
||||
}
|
||||
|
||||
__ MultiPop(kJSCallerSaved | ra.bit());
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
void UnaryOpStub::PrintName(StringStream* stream) {
|
||||
const char* op_name = Token::Name(op_);
|
||||
const char* overwrite_name = NULL; // Make g++ happy.
|
||||
@ -3484,12 +3542,12 @@ bool CEntryStub::IsPregenerated() {
|
||||
|
||||
|
||||
void CodeStub::GenerateStubsAheadOfTime() {
|
||||
WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime();
|
||||
}
|
||||
|
||||
|
||||
void CodeStub::GenerateFPStubs() {
|
||||
CEntryStub save_doubles(1);
|
||||
save_doubles.SaveDoubles();
|
||||
CEntryStub save_doubles(1, kSaveFPRegs);
|
||||
Handle<Code> code = save_doubles.GetCode();
|
||||
code->GetIsolate()->set_fp_stubs_generated(true);
|
||||
}
|
||||
@ -4812,16 +4870,25 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
||||
__ sw(a2, FieldMemOperand(last_match_info_elements,
|
||||
RegExpImpl::kLastCaptureCountOffset));
|
||||
// Store last subject and last input.
|
||||
__ mov(a3, last_match_info_elements); // Moved up to reduce latency.
|
||||
__ sw(subject,
|
||||
FieldMemOperand(last_match_info_elements,
|
||||
RegExpImpl::kLastSubjectOffset));
|
||||
__ RecordWrite(a3, Operand(RegExpImpl::kLastSubjectOffset), a2, t0);
|
||||
__ mov(a2, subject);
|
||||
__ RecordWriteField(last_match_info_elements,
|
||||
RegExpImpl::kLastSubjectOffset,
|
||||
a2,
|
||||
t3,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
__ sw(subject,
|
||||
FieldMemOperand(last_match_info_elements,
|
||||
RegExpImpl::kLastInputOffset));
|
||||
__ mov(a3, last_match_info_elements);
|
||||
__ RecordWrite(a3, Operand(RegExpImpl::kLastInputOffset), a2, t0);
|
||||
__ RecordWriteField(last_match_info_elements,
|
||||
RegExpImpl::kLastInputOffset,
|
||||
subject,
|
||||
t3,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
|
||||
// Get the static offsets vector filled by the native regexp code.
|
||||
ExternalReference address_of_static_offsets_vector =
|
||||
@ -7021,6 +7088,238 @@ void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
struct AheadOfTimeWriteBarrierStubList {
|
||||
Register object, value, address;
|
||||
RememberedSetAction action;
|
||||
};
|
||||
|
||||
|
||||
struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
|
||||
// TODO(1696): Fill this in for MIPS.
|
||||
// Null termination.
|
||||
{ no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET}
|
||||
};
|
||||
|
||||
|
||||
bool RecordWriteStub::CompilingCallsToThisStubIsGCSafe() {
|
||||
for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
|
||||
!entry->object.is(no_reg);
|
||||
entry++) {
|
||||
if (object_.is(entry->object) &&
|
||||
value_.is(entry->value) &&
|
||||
address_.is(entry->address) &&
|
||||
remembered_set_action_ == entry->action &&
|
||||
save_fp_regs_mode_ == kDontSaveFPRegs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true; // TODO(1696): Should be false.
|
||||
}
|
||||
|
||||
|
||||
void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() {
|
||||
StoreBufferOverflowStub stub1(kDontSaveFPRegs);
|
||||
stub1.GetCode();
|
||||
StoreBufferOverflowStub stub2(kSaveFPRegs);
|
||||
stub2.GetCode();
|
||||
}
|
||||
|
||||
|
||||
void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() {
|
||||
for (AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
|
||||
!entry->object.is(no_reg);
|
||||
entry++) {
|
||||
RecordWriteStub stub(entry->object,
|
||||
entry->value,
|
||||
entry->address,
|
||||
entry->action,
|
||||
kDontSaveFPRegs);
|
||||
stub.GetCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Takes the input in 3 registers: address_ value_ and object_. A pointer to
|
||||
// the value has just been written into the object, now this stub makes sure
|
||||
// we keep the GC informed. The word in the object where the value has been
|
||||
// written is in the address register.
|
||||
void RecordWriteStub::Generate(MacroAssembler* masm) {
|
||||
Label skip_to_incremental_noncompacting;
|
||||
Label skip_to_incremental_compacting;
|
||||
|
||||
// The first two branch+nop instructions are generated with labels so as to
|
||||
// get the offset fixed up correctly by the bind(Label*) call. We patch it
|
||||
// back and forth between a "bne zero_reg, zero_reg, ..." (a nop in this
|
||||
// position) and the "beq zero_reg, zero_reg, ..." when we start and stop
|
||||
// incremental heap marking.
|
||||
// See RecordWriteStub::Patch for details.
|
||||
__ beq(zero_reg, zero_reg, &skip_to_incremental_noncompacting);
|
||||
__ nop();
|
||||
__ beq(zero_reg, zero_reg, &skip_to_incremental_compacting);
|
||||
__ nop();
|
||||
|
||||
if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
|
||||
__ RememberedSetHelper(
|
||||
address_, value_, save_fp_regs_mode_, MacroAssembler::kReturnAtEnd);
|
||||
}
|
||||
__ Ret();
|
||||
|
||||
__ bind(&skip_to_incremental_noncompacting);
|
||||
GenerateIncremental(masm, INCREMENTAL);
|
||||
|
||||
__ bind(&skip_to_incremental_compacting);
|
||||
GenerateIncremental(masm, INCREMENTAL_COMPACTION);
|
||||
|
||||
// Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
|
||||
// Will be checked in IncrementalMarking::ActivateGeneratedStub.
|
||||
|
||||
PatchBranchIntoNop(masm, 0);
|
||||
PatchBranchIntoNop(masm, 2 * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
|
||||
void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
|
||||
regs_.Save(masm);
|
||||
|
||||
if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
|
||||
Label dont_need_remembered_set;
|
||||
|
||||
__ lw(regs_.scratch0(), MemOperand(regs_.address(), 0));
|
||||
__ JumpIfNotInNewSpace(regs_.scratch0(),
|
||||
regs_.scratch0(),
|
||||
&dont_need_remembered_set);
|
||||
|
||||
__ CheckPageFlag(regs_.object(),
|
||||
regs_.scratch0(),
|
||||
1 << MemoryChunk::SCAN_ON_SCAVENGE,
|
||||
ne,
|
||||
&dont_need_remembered_set);
|
||||
|
||||
// First notify the incremental marker if necessary, then update the
|
||||
// remembered set.
|
||||
CheckNeedsToInformIncrementalMarker(
|
||||
masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode);
|
||||
InformIncrementalMarker(masm, mode);
|
||||
regs_.Restore(masm);
|
||||
__ RememberedSetHelper(
|
||||
address_, value_, save_fp_regs_mode_, MacroAssembler::kReturnAtEnd);
|
||||
|
||||
__ bind(&dont_need_remembered_set);
|
||||
}
|
||||
|
||||
CheckNeedsToInformIncrementalMarker(
|
||||
masm, kReturnOnNoNeedToInformIncrementalMarker, mode);
|
||||
InformIncrementalMarker(masm, mode);
|
||||
regs_.Restore(masm);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
|
||||
regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
|
||||
int argument_count = 3;
|
||||
__ PrepareCallCFunction(argument_count, regs_.scratch0());
|
||||
Register address =
|
||||
a0.is(regs_.address()) ? regs_.scratch0() : regs_.address();
|
||||
ASSERT(!address.is(regs_.object()));
|
||||
ASSERT(!address.is(a0));
|
||||
__ Move(address, regs_.address());
|
||||
__ Move(a0, regs_.object());
|
||||
if (mode == INCREMENTAL_COMPACTION) {
|
||||
__ Move(a1, address);
|
||||
} else {
|
||||
ASSERT(mode == INCREMENTAL);
|
||||
__ lw(a1, MemOperand(address, 0));
|
||||
}
|
||||
__ li(a2, Operand(ExternalReference::isolate_address()));
|
||||
|
||||
AllowExternalCallThatCantCauseGC scope(masm);
|
||||
if (mode == INCREMENTAL_COMPACTION) {
|
||||
__ CallCFunction(
|
||||
ExternalReference::incremental_evacuation_record_write_function(
|
||||
masm->isolate()),
|
||||
argument_count);
|
||||
} else {
|
||||
ASSERT(mode == INCREMENTAL);
|
||||
__ CallCFunction(
|
||||
ExternalReference::incremental_marking_record_write_function(
|
||||
masm->isolate()),
|
||||
argument_count);
|
||||
}
|
||||
regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
|
||||
}
|
||||
|
||||
|
||||
void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
|
||||
MacroAssembler* masm,
|
||||
OnNoNeedToInformIncrementalMarker on_no_need,
|
||||
Mode mode) {
|
||||
Label on_black;
|
||||
Label need_incremental;
|
||||
Label need_incremental_pop_scratch;
|
||||
|
||||
// Let's look at the color of the object: If it is not black we don't have
|
||||
// to inform the incremental marker.
|
||||
__ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black);
|
||||
|
||||
regs_.Restore(masm);
|
||||
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
|
||||
__ RememberedSetHelper(
|
||||
address_, value_, save_fp_regs_mode_, MacroAssembler::kReturnAtEnd);
|
||||
} else {
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
__ bind(&on_black);
|
||||
|
||||
// Get the value from the slot.
|
||||
__ lw(regs_.scratch0(), MemOperand(regs_.address(), 0));
|
||||
|
||||
if (mode == INCREMENTAL_COMPACTION) {
|
||||
Label ensure_not_white;
|
||||
|
||||
__ CheckPageFlag(regs_.scratch0(), // Contains value.
|
||||
regs_.scratch1(), // Scratch.
|
||||
MemoryChunk::kEvacuationCandidateMask,
|
||||
eq,
|
||||
&ensure_not_white);
|
||||
|
||||
__ CheckPageFlag(regs_.object(),
|
||||
regs_.scratch1(), // Scratch.
|
||||
MemoryChunk::kSkipEvacuationSlotsRecordingMask,
|
||||
eq,
|
||||
&need_incremental);
|
||||
|
||||
__ bind(&ensure_not_white);
|
||||
}
|
||||
|
||||
// We need extra registers for this, so we push the object and the address
|
||||
// register temporarily.
|
||||
__ Push(regs_.object(), regs_.address());
|
||||
__ EnsureNotWhite(regs_.scratch0(), // The value.
|
||||
regs_.scratch1(), // Scratch.
|
||||
regs_.object(), // Scratch.
|
||||
regs_.address(), // Scratch.
|
||||
&need_incremental_pop_scratch);
|
||||
__ Pop(regs_.object(), regs_.address());
|
||||
|
||||
regs_.Restore(masm);
|
||||
if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
|
||||
__ RememberedSetHelper(
|
||||
address_, value_, save_fp_regs_mode_, MacroAssembler::kReturnAtEnd);
|
||||
} else {
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
__ bind(&need_incremental_pop_scratch);
|
||||
__ Pop(regs_.object(), regs_.address());
|
||||
|
||||
__ bind(&need_incremental);
|
||||
|
||||
// Fall through when we need to inform the incremental marker.
|
||||
}
|
||||
|
||||
|
||||
#undef __
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -59,6 +59,25 @@ class TranscendentalCacheStub: public CodeStub {
|
||||
};
|
||||
|
||||
|
||||
class StoreBufferOverflowStub: public CodeStub {
|
||||
public:
|
||||
explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
|
||||
: save_doubles_(save_fp) { }
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
virtual bool CompilingCallsToThisStubIsGCSafe() { return true; }
|
||||
static void GenerateFixedRegStubsAheadOfTime();
|
||||
virtual bool SometimesSetsUpAFrame() { return false; }
|
||||
|
||||
private:
|
||||
SaveFPRegsMode save_doubles_;
|
||||
|
||||
Major MajorKey() { return StoreBufferOverflow; }
|
||||
int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
|
||||
};
|
||||
|
||||
|
||||
class UnaryOpStub: public CodeStub {
|
||||
public:
|
||||
UnaryOpStub(Token::Value op,
|
||||
@ -324,7 +343,15 @@ class WriteInt32ToHeapNumberStub : public CodeStub {
|
||||
: the_int_(the_int),
|
||||
the_heap_number_(the_heap_number),
|
||||
scratch_(scratch),
|
||||
sign_(scratch2) { }
|
||||
sign_(scratch2) {
|
||||
ASSERT(IntRegisterBits::is_valid(the_int_.code()));
|
||||
ASSERT(HeapNumberRegisterBits::is_valid(the_heap_number_.code()));
|
||||
ASSERT(ScratchRegisterBits::is_valid(scratch_.code()));
|
||||
ASSERT(SignRegisterBits::is_valid(sign_.code()));
|
||||
}
|
||||
|
||||
bool CompilingCallsToThisStubIsGCSafe();
|
||||
static void GenerateFixedRegStubsAheadOfTime();
|
||||
|
||||
private:
|
||||
Register the_int_;
|
||||
@ -375,6 +402,215 @@ class NumberToStringStub: public CodeStub {
|
||||
};
|
||||
|
||||
|
||||
class RecordWriteStub: public CodeStub {
|
||||
public:
|
||||
RecordWriteStub(Register object,
|
||||
Register value,
|
||||
Register address,
|
||||
RememberedSetAction remembered_set_action,
|
||||
SaveFPRegsMode fp_mode)
|
||||
: object_(object),
|
||||
value_(value),
|
||||
address_(address),
|
||||
remembered_set_action_(remembered_set_action),
|
||||
save_fp_regs_mode_(fp_mode),
|
||||
regs_(object, // An input reg.
|
||||
address, // An input reg.
|
||||
value) { // One scratch reg.
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
STORE_BUFFER_ONLY,
|
||||
INCREMENTAL,
|
||||
INCREMENTAL_COMPACTION
|
||||
};
|
||||
|
||||
virtual bool CompilingCallsToThisStubIsGCSafe();
|
||||
static void GenerateFixedRegStubsAheadOfTime();
|
||||
virtual bool SometimesSetsUpAFrame() { return false; }
|
||||
|
||||
static void PatchBranchIntoNop(MacroAssembler* masm, int pos) {
|
||||
const unsigned offset = masm->instr_at(pos) & kImm16Mask;
|
||||
masm->instr_at_put(pos, BNE | (zero_reg.code() << kRsShift) |
|
||||
(zero_reg.code() << kRtShift) | (offset & kImm16Mask));
|
||||
ASSERT(Assembler::IsBne(masm->instr_at(pos)));
|
||||
}
|
||||
|
||||
static void PatchNopIntoBranch(MacroAssembler* masm, int pos) {
|
||||
const unsigned offset = masm->instr_at(pos) & kImm16Mask;
|
||||
masm->instr_at_put(pos, BEQ | (zero_reg.code() << kRsShift) |
|
||||
(zero_reg.code() << kRtShift) | (offset & kImm16Mask));
|
||||
ASSERT(Assembler::IsBeq(masm->instr_at(pos)));
|
||||
}
|
||||
|
||||
static Mode GetMode(Code* stub) {
|
||||
Instr first_instruction = Assembler::instr_at(stub->instruction_start());
|
||||
Instr second_instruction = Assembler::instr_at(stub->instruction_start() +
|
||||
2 * Assembler::kInstrSize);
|
||||
|
||||
if (Assembler::IsBeq(first_instruction)) {
|
||||
return INCREMENTAL;
|
||||
}
|
||||
|
||||
ASSERT(Assembler::IsBne(first_instruction));
|
||||
|
||||
if (Assembler::IsBeq(second_instruction)) {
|
||||
return INCREMENTAL_COMPACTION;
|
||||
}
|
||||
|
||||
ASSERT(Assembler::IsBne(second_instruction));
|
||||
|
||||
return STORE_BUFFER_ONLY;
|
||||
}
|
||||
|
||||
static void Patch(Code* stub, Mode mode) {
|
||||
MacroAssembler masm(NULL,
|
||||
stub->instruction_start(),
|
||||
stub->instruction_size());
|
||||
switch (mode) {
|
||||
case STORE_BUFFER_ONLY:
|
||||
ASSERT(GetMode(stub) == INCREMENTAL ||
|
||||
GetMode(stub) == INCREMENTAL_COMPACTION);
|
||||
PatchBranchIntoNop(&masm, 0);
|
||||
PatchBranchIntoNop(&masm, 2 * Assembler::kInstrSize);
|
||||
break;
|
||||
case INCREMENTAL:
|
||||
ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
|
||||
PatchNopIntoBranch(&masm, 0);
|
||||
break;
|
||||
case INCREMENTAL_COMPACTION:
|
||||
ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
|
||||
PatchNopIntoBranch(&masm, 2 * Assembler::kInstrSize);
|
||||
break;
|
||||
}
|
||||
ASSERT(GetMode(stub) == mode);
|
||||
CPU::FlushICache(stub->instruction_start(), 4 * Assembler::kInstrSize);
|
||||
}
|
||||
|
||||
private:
|
||||
// This is a helper class for freeing up 3 scratch registers. The input is
|
||||
// two registers that must be preserved and one scratch register provided by
|
||||
// the caller.
|
||||
class RegisterAllocation {
|
||||
public:
|
||||
RegisterAllocation(Register object,
|
||||
Register address,
|
||||
Register scratch0)
|
||||
: object_(object),
|
||||
address_(address),
|
||||
scratch0_(scratch0) {
|
||||
ASSERT(!AreAliased(scratch0, object, address, no_reg));
|
||||
scratch1_ = GetRegThatIsNotOneOf(object_, address_, scratch0_);
|
||||
}
|
||||
|
||||
void Save(MacroAssembler* masm) {
|
||||
ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
|
||||
// We don't have to save scratch0_ because it was given to us as
|
||||
// a scratch register.
|
||||
masm->push(scratch1_);
|
||||
}
|
||||
|
||||
void Restore(MacroAssembler* masm) {
|
||||
masm->pop(scratch1_);
|
||||
}
|
||||
|
||||
// If we have to call into C then we need to save and restore all caller-
|
||||
// saved registers that were not already preserved. The scratch registers
|
||||
// will be restored by other means so we don't bother pushing them here.
|
||||
void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
|
||||
masm->MultiPush((kJSCallerSaved | ra.bit()) & ~scratch1_.bit());
|
||||
if (mode == kSaveFPRegs) {
|
||||
CpuFeatures::Scope scope(FPU);
|
||||
masm->MultiPushFPU(kCallerSavedFPU);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
|
||||
SaveFPRegsMode mode) {
|
||||
if (mode == kSaveFPRegs) {
|
||||
CpuFeatures::Scope scope(FPU);
|
||||
masm->MultiPopFPU(kCallerSavedFPU);
|
||||
}
|
||||
masm->MultiPop((kJSCallerSaved | ra.bit()) & ~scratch1_.bit());
|
||||
}
|
||||
|
||||
inline Register object() { return object_; }
|
||||
inline Register address() { return address_; }
|
||||
inline Register scratch0() { return scratch0_; }
|
||||
inline Register scratch1() { return scratch1_; }
|
||||
|
||||
private:
|
||||
Register object_;
|
||||
Register address_;
|
||||
Register scratch0_;
|
||||
Register scratch1_;
|
||||
|
||||
Register GetRegThatIsNotOneOf(Register r1,
|
||||
Register r2,
|
||||
Register r3) {
|
||||
for (int i = 0; i < Register::kNumAllocatableRegisters; i++) {
|
||||
Register candidate = Register::FromAllocationIndex(i);
|
||||
if (candidate.is(r1)) continue;
|
||||
if (candidate.is(r2)) continue;
|
||||
if (candidate.is(r3)) continue;
|
||||
return candidate;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return no_reg;
|
||||
}
|
||||
friend class RecordWriteStub;
|
||||
};
|
||||
|
||||
enum OnNoNeedToInformIncrementalMarker {
|
||||
kReturnOnNoNeedToInformIncrementalMarker,
|
||||
kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
|
||||
};
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
void GenerateIncremental(MacroAssembler* masm, Mode mode);
|
||||
void CheckNeedsToInformIncrementalMarker(
|
||||
MacroAssembler* masm,
|
||||
OnNoNeedToInformIncrementalMarker on_no_need,
|
||||
Mode mode);
|
||||
void InformIncrementalMarker(MacroAssembler* masm, Mode mode);
|
||||
|
||||
Major MajorKey() { return RecordWrite; }
|
||||
|
||||
int MinorKey() {
|
||||
return ObjectBits::encode(object_.code()) |
|
||||
ValueBits::encode(value_.code()) |
|
||||
AddressBits::encode(address_.code()) |
|
||||
RememberedSetActionBits::encode(remembered_set_action_) |
|
||||
SaveFPRegsModeBits::encode(save_fp_regs_mode_);
|
||||
}
|
||||
|
||||
bool MustBeInStubCache() {
|
||||
// All stubs must be registered in the stub cache
|
||||
// otherwise IncrementalMarker would not be able to find
|
||||
// and patch it.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Activate(Code* code) {
|
||||
code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
|
||||
}
|
||||
|
||||
class ObjectBits: public BitField<int, 0, 5> {};
|
||||
class ValueBits: public BitField<int, 5, 5> {};
|
||||
class AddressBits: public BitField<int, 10, 5> {};
|
||||
class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {};
|
||||
class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {};
|
||||
|
||||
Register object_;
|
||||
Register value_;
|
||||
Register address_;
|
||||
RememberedSetAction remembered_set_action_;
|
||||
SaveFPRegsMode save_fp_regs_mode_;
|
||||
Label slow_;
|
||||
RegisterAllocation regs_;
|
||||
};
|
||||
|
||||
|
||||
// Enter C code from generated RegExp code in a way that allows
|
||||
// the C code to fix the return address in case of a GC.
|
||||
// Currently only needed on ARM and MIPS.
|
||||
|
@ -71,21 +71,6 @@ class CodeGenerator: public AstVisitor {
|
||||
int pos,
|
||||
bool right_here = false);
|
||||
|
||||
// Constants related to patching of inlined load/store.
|
||||
static int GetInlinedKeyedLoadInstructionsAfterPatch() {
|
||||
// This is in correlation with the padding in MacroAssembler::Abort.
|
||||
return FLAG_debug_code ? 45 : 20;
|
||||
}
|
||||
|
||||
static const int kInlinedKeyedStoreInstructionsAfterPatch = 13;
|
||||
|
||||
static int GetInlinedNamedStoreInstructionsAfterPatch() {
|
||||
ASSERT(Isolate::Current()->inlined_write_barrier_size() != -1);
|
||||
// Magic number 5: instruction count after patched map load:
|
||||
// li: 2 (liu & ori), Branch : 2 (bne & nop), sw : 1
|
||||
return Isolate::Current()->inlined_write_barrier_size() + 5;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
|
||||
};
|
||||
|
@ -53,7 +53,8 @@ void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
|
||||
}
|
||||
|
||||
|
||||
void Deoptimizer::PatchStackCheckCodeAt(Address pc_after,
|
||||
void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code,
|
||||
Address pc_after,
|
||||
Code* check_code,
|
||||
Code* replacement_code) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -85,6 +85,20 @@ static const RegList kCalleeSavedFPU =
|
||||
1 << 30; // f30
|
||||
|
||||
static const int kNumCalleeSavedFPU = 6;
|
||||
|
||||
static const RegList kCallerSavedFPU =
|
||||
1 << 0 | // f0
|
||||
1 << 2 | // f2
|
||||
1 << 4 | // f4
|
||||
1 << 6 | // f6
|
||||
1 << 8 | // f8
|
||||
1 << 10 | // f10
|
||||
1 << 12 | // f12
|
||||
1 << 14 | // f14
|
||||
1 << 16 | // f16
|
||||
1 << 18; // f18
|
||||
|
||||
|
||||
// Number of registers for which space is reserved in safepoints. Must be a
|
||||
// multiple of 8.
|
||||
static const int kNumSafepointRegisters = 24;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "stub-cache.h"
|
||||
|
||||
#include "mips/code-stubs-mips.h"
|
||||
#include "mips/macro-assembler-mips.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -214,14 +215,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
|
||||
// Load parameter from stack.
|
||||
__ lw(a0, MemOperand(fp, parameter_offset));
|
||||
// Store it in the context.
|
||||
__ li(a1, Operand(Context::SlotOffset(var->index())));
|
||||
__ addu(a2, cp, a1);
|
||||
__ sw(a0, MemOperand(a2, 0));
|
||||
// Update the write barrier. This clobbers all involved
|
||||
// registers, so we have to use two more registers to avoid
|
||||
// clobbering cp.
|
||||
__ mov(a2, cp);
|
||||
__ RecordWrite(a2, a1, a3);
|
||||
MemOperand target = ContextOperand(cp, var->index());
|
||||
__ sw(a0, target);
|
||||
|
||||
// Update the write barrier.
|
||||
__ RecordWriteContextSlot(
|
||||
cp, target.offset(), a0, a3, kRAHasBeenSaved, kDontSaveFPRegs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -685,10 +684,12 @@ void FullCodeGenerator::SetVar(Variable* var,
|
||||
__ sw(src, location);
|
||||
// Emit the write barrier code if the location is in the heap.
|
||||
if (var->IsContextSlot()) {
|
||||
__ RecordWrite(scratch0,
|
||||
Operand(Context::SlotOffset(var->index())),
|
||||
scratch1,
|
||||
src);
|
||||
__ RecordWriteContextSlot(scratch0,
|
||||
location.offset(),
|
||||
src,
|
||||
scratch1,
|
||||
kRAHasBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -765,8 +766,14 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
__ sw(result_register(), ContextOperand(cp, variable->index()));
|
||||
int offset = Context::SlotOffset(variable->index());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ mov(a1, cp);
|
||||
__ RecordWrite(a1, Operand(offset), a2, result_register());
|
||||
__ RecordWriteContextSlot(cp,
|
||||
offset,
|
||||
result_register(),
|
||||
a2,
|
||||
kRAHasBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
} else if (mode == Variable::CONST || mode == Variable::LET) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
@ -1513,7 +1520,8 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
|
||||
|
||||
// Update the write barrier for the array store with v0 as the scratch
|
||||
// register.
|
||||
__ RecordWrite(a1, Operand(offset), a2, result_register());
|
||||
__ RecordWriteField(
|
||||
a1, offset, result_register(), a2, kRAHasBeenSaved, kDontSaveFPRegs);
|
||||
|
||||
PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
|
||||
}
|
||||
@ -1890,7 +1898,8 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
// RecordWrite may destroy all its register arguments.
|
||||
__ mov(a3, result_register());
|
||||
int offset = Context::SlotOffset(var->index());
|
||||
__ RecordWrite(a1, Operand(offset), a2, a3);
|
||||
__ RecordWriteContextSlot(
|
||||
a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1908,7 +1917,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
|
||||
__ sw(v0, location);
|
||||
if (var->IsContextSlot()) {
|
||||
__ mov(a3, v0);
|
||||
__ RecordWrite(a1, Operand(Context::SlotOffset(var->index())), a2, a3);
|
||||
int offset = Context::SlotOffset(var->index());
|
||||
__ RecordWriteContextSlot(
|
||||
a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs);
|
||||
}
|
||||
} else {
|
||||
ASSERT(var->IsLookupSlot());
|
||||
@ -2874,7 +2885,9 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
|
||||
__ sw(v0, FieldMemOperand(a1, JSValue::kValueOffset));
|
||||
// Update the write barrier. Save the value as it will be
|
||||
// overwritten by the write barrier code and is needed afterward.
|
||||
__ RecordWrite(a1, Operand(JSValue::kValueOffset - kHeapObjectTag), a2, a3);
|
||||
__ mov(a2, v0);
|
||||
__ RecordWriteField(
|
||||
a1, JSValue::kValueOffset, a2, a3, kRAHasBeenSaved, kDontSaveFPRegs);
|
||||
|
||||
__ bind(&done);
|
||||
context()->Plug(v0);
|
||||
@ -3167,16 +3180,25 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
|
||||
__ sw(scratch1, MemOperand(index2, 0));
|
||||
__ sw(scratch2, MemOperand(index1, 0));
|
||||
|
||||
Label new_space;
|
||||
__ InNewSpace(elements, scratch1, eq, &new_space);
|
||||
Label no_remembered_set;
|
||||
__ CheckPageFlag(elements,
|
||||
scratch1,
|
||||
1 << MemoryChunk::SCAN_ON_SCAVENGE,
|
||||
ne,
|
||||
&no_remembered_set);
|
||||
// Possible optimization: do a check that both values are Smis
|
||||
// (or them and test against Smi mask).
|
||||
|
||||
__ mov(scratch1, elements);
|
||||
__ RecordWriteHelper(elements, index1, scratch2);
|
||||
__ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements.
|
||||
// We are swapping two objects in an array and the incremental marker never
|
||||
// pauses in the middle of scanning a single object. Therefore the
|
||||
// incremental marker is not disturbed, so we don't need to call the
|
||||
// RecordWrite stub that notifies the incremental marker.
|
||||
__ RememberedSetHelper(
|
||||
index1, scratch2, kDontSaveFPRegs, MacroAssembler::kFallThroughAtEnd);
|
||||
__ RememberedSetHelper(
|
||||
index2, scratch2, kDontSaveFPRegs, MacroAssembler::kFallThroughAtEnd);
|
||||
|
||||
__ bind(&new_space);
|
||||
__ bind(&no_remembered_set);
|
||||
// We are done. Drop elements from the stack, and return undefined.
|
||||
__ Drop(3);
|
||||
__ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
|
||||
|
@ -210,7 +210,8 @@ static void GenerateDictionaryStore(MacroAssembler* masm,
|
||||
|
||||
// Update the write barrier. Make sure not to clobber the value.
|
||||
__ mov(scratch1, value);
|
||||
__ RecordWrite(elements, scratch2, scratch1);
|
||||
__ RecordWrite(
|
||||
elements, scratch2, scratch1, kRAHasNotBeenSaved, kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
|
||||
@ -904,9 +905,9 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
|
||||
MemOperand mapped_location =
|
||||
GenerateMappedArgumentsLookup(masm, a2, a1, a3, t0, t1, ¬in, &slow);
|
||||
__ sw(a0, mapped_location);
|
||||
// Verify mapped_location MemOperand is register, with no offset.
|
||||
ASSERT_EQ(mapped_location.offset(), 0);
|
||||
__ RecordWrite(a3, mapped_location.rm(), t5);
|
||||
__ Addu(t2, a3, t1);
|
||||
__ mov(t5, a0);
|
||||
__ RecordWrite(a3, t2, t5, kRAHasNotBeenSaved, kDontSaveFPRegs);
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ mov(v0, a0); // (In delay slot) return the value stored in v0.
|
||||
__ bind(¬in);
|
||||
@ -914,8 +915,9 @@ void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
|
||||
MemOperand unmapped_location =
|
||||
GenerateUnmappedArgumentsLookup(masm, a1, a3, t0, &slow);
|
||||
__ sw(a0, unmapped_location);
|
||||
ASSERT_EQ(unmapped_location.offset(), 0);
|
||||
__ RecordWrite(a3, unmapped_location.rm(), t5);
|
||||
__ Addu(t2, a3, t0);
|
||||
__ mov(t5, a0);
|
||||
__ RecordWrite(a3, t2, t5, kRAHasNotBeenSaved, kDontSaveFPRegs);
|
||||
__ Ret(USE_DELAY_SLOT);
|
||||
__ mov(v0, a0); // (In delay slot) return the value stored in v0.
|
||||
__ bind(&slow);
|
||||
@ -1290,18 +1292,25 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
|
||||
// Fall through to fast case.
|
||||
|
||||
__ bind(&fast);
|
||||
Register scratch_value = t0;
|
||||
Register address = t1;
|
||||
// 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));
|
||||
__ Addu(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
__ sll(scratch_value, key, kPointerSizeLog2 - kSmiTagSize);
|
||||
__ Addu(address, address, scratch_value);
|
||||
__ sw(value, MemOperand(address));
|
||||
// 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);
|
||||
__ mov(scratch_value, value); // Preserve the value which is returned.
|
||||
__ RecordWrite(elements,
|
||||
address,
|
||||
scratch_value,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
__ bind(&exit);
|
||||
|
||||
__ mov(v0, a0); // Return the value written.
|
||||
|
@ -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),
|
||||
|
@ -92,6 +92,13 @@ enum BranchDelaySlot {
|
||||
};
|
||||
|
||||
|
||||
enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
|
||||
enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
|
||||
enum RAStatus { kRAHasNotBeenSaved, kRAHasBeenSaved };
|
||||
|
||||
bool AreAliased(Register r1, Register r2, Register r3, Register r4);
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Static helper functions.
|
||||
|
||||
@ -240,6 +247,7 @@ class MacroAssembler: public Assembler {
|
||||
Branch(L);
|
||||
}
|
||||
|
||||
|
||||
// Load an object from the root table.
|
||||
void LoadRoot(Register destination,
|
||||
Heap::RootListIndex index);
|
||||
@ -255,39 +263,126 @@ class MacroAssembler: public Assembler {
|
||||
Condition cond, Register src1, const Operand& src2);
|
||||
|
||||
|
||||
// Check if object is in new space.
|
||||
// scratch can be object itself, but it will be clobbered.
|
||||
void InNewSpace(Register object,
|
||||
Register scratch,
|
||||
Condition cc, // eq for new space, ne otherwise.
|
||||
Label* branch);
|
||||
// ---------------------------------------------------------------------------
|
||||
// GC Support
|
||||
|
||||
void IncrementalMarkingRecordWriteHelper(Register object,
|
||||
Register value,
|
||||
Register address);
|
||||
|
||||
enum RememberedSetFinalAction {
|
||||
kReturnAtEnd,
|
||||
kFallThroughAtEnd
|
||||
};
|
||||
|
||||
|
||||
// For the page containing |object| mark the region covering [address]
|
||||
// dirty. The object address must be in the first 8K of an allocated page.
|
||||
void RecordWriteHelper(Register object,
|
||||
Register address,
|
||||
Register scratch);
|
||||
// Record in the remembered set the fact that we have a pointer to new space
|
||||
// at the address pointed to by the addr register. Only works if addr is not
|
||||
// in new space.
|
||||
void RememberedSetHelper(Register addr,
|
||||
Register scratch,
|
||||
SaveFPRegsMode save_fp,
|
||||
RememberedSetFinalAction and_then);
|
||||
|
||||
// For the page containing |object| mark the region covering
|
||||
// [object+offset] dirty. The object address must be in the first 8K
|
||||
// of an allocated page. The 'scratch' registers are used in the
|
||||
// implementation and all 3 registers are clobbered by the
|
||||
// operation, as well as the 'at' register. RecordWrite updates the
|
||||
// write barrier even when storing smis.
|
||||
void RecordWrite(Register object,
|
||||
Operand offset,
|
||||
void CheckPageFlag(Register object,
|
||||
Register scratch,
|
||||
int mask,
|
||||
Condition cc,
|
||||
Label* condition_met);
|
||||
|
||||
// Check if object is in new space. Jumps if the object is not in new space.
|
||||
// The register scratch can be object itself, but it will be clobbered.
|
||||
void JumpIfNotInNewSpace(Register object,
|
||||
Register scratch,
|
||||
Label* branch) {
|
||||
InNewSpace(object, scratch, ne, branch);
|
||||
}
|
||||
|
||||
// Check if object is in new space. Jumps if the object is in new space.
|
||||
// The register scratch can be object itself, but it will be clobbered.
|
||||
void JumpIfInNewSpace(Register object,
|
||||
Register scratch,
|
||||
Label* branch) {
|
||||
InNewSpace(object, scratch, eq, branch);
|
||||
}
|
||||
|
||||
// Check if an object has a given incremental marking color.
|
||||
void HasColor(Register object,
|
||||
Register scratch0,
|
||||
Register scratch1,
|
||||
Label* has_color,
|
||||
int first_bit,
|
||||
int second_bit);
|
||||
|
||||
void JumpIfBlack(Register object,
|
||||
Register scratch0,
|
||||
Register scratch1);
|
||||
Register scratch1,
|
||||
Label* on_black);
|
||||
|
||||
// For the page containing |object| mark the region covering
|
||||
// [address] dirty. The object address must be in the first 8K of an
|
||||
// allocated page. All 3 registers are clobbered by the operation,
|
||||
// as well as the ip register. RecordWrite updates the write barrier
|
||||
// even when storing smis.
|
||||
void RecordWrite(Register object,
|
||||
Register address,
|
||||
Register scratch);
|
||||
// Checks the color of an object. If the object is already grey or black
|
||||
// then we just fall through, since it is already live. If it is white and
|
||||
// we can determine that it doesn't need to be scanned, then we just mark it
|
||||
// black and fall through. For the rest we jump to the label so the
|
||||
// incremental marker can fix its assumptions.
|
||||
void EnsureNotWhite(Register object,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Register scratch3,
|
||||
Label* object_is_white_and_not_data);
|
||||
|
||||
// Detects conservatively whether an object is data-only, ie it does need to
|
||||
// be scanned by the garbage collector.
|
||||
void JumpIfDataObject(Register value,
|
||||
Register scratch,
|
||||
Label* not_data_object);
|
||||
|
||||
// Notify the garbage collector that we wrote a pointer into an object.
|
||||
// |object| is the object being stored into, |value| is the object being
|
||||
// stored. value and scratch registers are clobbered by the operation.
|
||||
// The offset is the offset from the start of the object, not the offset from
|
||||
// the tagged HeapObject pointer. For use with FieldOperand(reg, off).
|
||||
void RecordWriteField(
|
||||
Register object,
|
||||
int offset,
|
||||
Register value,
|
||||
Register scratch,
|
||||
RAStatus ra_status,
|
||||
SaveFPRegsMode save_fp,
|
||||
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
|
||||
SmiCheck smi_check = INLINE_SMI_CHECK);
|
||||
|
||||
// As above, but the offset has the tag presubtracted. For use with
|
||||
// MemOperand(reg, off).
|
||||
inline void RecordWriteContextSlot(
|
||||
Register context,
|
||||
int offset,
|
||||
Register value,
|
||||
Register scratch,
|
||||
RAStatus ra_status,
|
||||
SaveFPRegsMode save_fp,
|
||||
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
|
||||
SmiCheck smi_check = INLINE_SMI_CHECK) {
|
||||
RecordWriteField(context,
|
||||
offset + kHeapObjectTag,
|
||||
value,
|
||||
scratch,
|
||||
ra_status,
|
||||
save_fp,
|
||||
remembered_set_action,
|
||||
smi_check);
|
||||
}
|
||||
|
||||
// For a given |object| notify the garbage collector that the slot |address|
|
||||
// has been written. |value| is the object being stored. The value and
|
||||
// address registers are clobbered by the operation.
|
||||
void RecordWrite(
|
||||
Register object,
|
||||
Register address,
|
||||
Register value,
|
||||
RAStatus ra_status,
|
||||
SaveFPRegsMode save_fp,
|
||||
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
|
||||
SmiCheck smi_check = INLINE_SMI_CHECK);
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -1266,6 +1361,19 @@ class MacroAssembler: public Assembler {
|
||||
Register scratch1,
|
||||
Register scratch2);
|
||||
|
||||
// Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace.
|
||||
void InNewSpace(Register object,
|
||||
Register scratch,
|
||||
Condition cond, // eq for new space, ne otherwise.
|
||||
Label* branch);
|
||||
|
||||
// Helper for finding the mark bits for an address. Afterwards, the
|
||||
// bitmap register points at the word with the mark bits and the mask
|
||||
// the position of the first bit. Leaves addr_reg unchanged.
|
||||
inline void GetMarkBits(Register addr_reg,
|
||||
Register bitmap_reg,
|
||||
Register mask_reg);
|
||||
|
||||
// Compute memory operands for safepoint stack slots.
|
||||
static int SafepointRegisterStackIndex(int reg_code);
|
||||
MemOperand SafepointRegisterSlot(Register reg);
|
||||
|
@ -432,7 +432,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
|
||||
// Update the write barrier for the array address.
|
||||
// Pass the now unused name_reg as a scratch register.
|
||||
__ RecordWrite(receiver_reg, Operand(offset), name_reg, scratch);
|
||||
__ mov(name_reg, a0);
|
||||
__ RecordWriteField(receiver_reg,
|
||||
offset,
|
||||
name_reg,
|
||||
scratch,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
} else {
|
||||
// Write to the properties array.
|
||||
int offset = index * kPointerSize + FixedArray::kHeaderSize;
|
||||
@ -445,7 +451,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
||||
|
||||
// Update the write barrier for the array address.
|
||||
// Ok to clobber receiver_reg and name_reg, since we return.
|
||||
__ RecordWrite(scratch, Operand(offset), name_reg, receiver_reg);
|
||||
__ mov(name_reg, a0);
|
||||
__ RecordWriteField(scratch,
|
||||
offset,
|
||||
name_reg,
|
||||
receiver_reg,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
}
|
||||
|
||||
// Return the value (register v0).
|
||||
@ -1589,7 +1601,7 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
DONT_DO_SMI_CHECK);
|
||||
|
||||
if (argc == 1) { // Otherwise fall through to call the builtin.
|
||||
Label exit, with_write_barrier, attempt_to_grow_elements;
|
||||
Label exit, attempt_to_grow_elements;
|
||||
|
||||
// Get the array's length into v0 and calculate new length.
|
||||
__ lw(v0, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
||||
@ -1618,14 +1630,20 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object,
|
||||
__ sw(t0, MemOperand(end_elements));
|
||||
|
||||
// Check for a smi.
|
||||
Label with_write_barrier;
|
||||
__ JumpIfNotSmi(t0, &with_write_barrier);
|
||||
__ bind(&exit);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
|
||||
__ bind(&with_write_barrier);
|
||||
__ InNewSpace(elements, t0, eq, &exit);
|
||||
__ RecordWriteHelper(elements, end_elements, t0);
|
||||
__ RecordWrite(elements,
|
||||
end_elements,
|
||||
t0,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
__ Drop(argc + 1);
|
||||
__ Ret();
|
||||
|
||||
@ -2732,6 +2750,16 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
|
||||
// Store the value in the cell.
|
||||
__ sw(a0, FieldMemOperand(t0, JSGlobalPropertyCell::kValueOffset));
|
||||
__ mov(v0, a0); // Stored value must be returned in v0.
|
||||
|
||||
// This trashes a0 but the value is returned in v0 anyway.
|
||||
__ RecordWriteField(t0,
|
||||
JSGlobalPropertyCell::kValueOffset,
|
||||
a0,
|
||||
a2,
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
OMIT_REMEMBERED_SET);
|
||||
|
||||
Counters* counters = masm()->isolate()->counters();
|
||||
__ IncrementCounter(counters->named_store_global_inline(), 1, a1, a3);
|
||||
__ Ret();
|
||||
@ -4360,9 +4388,14 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm,
|
||||
elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
|
||||
__ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize);
|
||||
__ Addu(scratch3, scratch2, scratch);
|
||||
__ sw(value_reg, MemOperand(scratch3));
|
||||
__ RecordWrite(scratch, Operand(scratch2), receiver_reg , elements_reg);
|
||||
__ Addu(scratch, scratch, scratch2);
|
||||
__ sw(value_reg, MemOperand(scratch));
|
||||
__ mov(receiver_reg, value_reg);
|
||||
__ RecordWrite(elements_reg, // Object.
|
||||
scratch, // Address.
|
||||
receiver_reg, // Value.
|
||||
kRAHasNotBeenSaved,
|
||||
kDontSaveFPRegs);
|
||||
|
||||
// value_reg (a0) is preserved.
|
||||
// Done.
|
||||
|
Loading…
Reference in New Issue
Block a user