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:
danno@chromium.org 2011-10-13 07:35:38 +00:00
parent 6112eb9bda
commit 4f7d11f963
12 changed files with 1151 additions and 177 deletions

View File

@ -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) {

View File

@ -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 {

View File

@ -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

View File

@ -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.

View File

@ -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);
};

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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, &notin, &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(&notin);
@ -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.

View File

@ -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, &not_in_new_space);
Abort("new-space object passed to RecordWriteHelper");
bind(&not_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),

View File

@ -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);

View File

@ -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.