| Index: src/arm/code-stubs-arm.cc
|
| ===================================================================
|
| --- src/arm/code-stubs-arm.cc (revision 9531)
|
| +++ src/arm/code-stubs-arm.cc (working copy)
|
| @@ -838,9 +838,11 @@
|
| __ vmov(d0, r0, r1);
|
| __ vmov(d1, r2, r3);
|
| }
|
| - // Call C routine that may not cause GC or other trouble.
|
| - __ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()),
|
| - 0, 2);
|
| + {
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ CallCFunction(
|
| + ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2);
|
| + }
|
| // Store answer in the overwritable heap number. Double returned in
|
| // registers r0 and r1 or in d0.
|
| if (masm->use_eabi_hardfloat()) {
|
| @@ -857,6 +859,29 @@
|
| }
|
|
|
|
|
| +bool WriteInt32ToHeapNumberStub::IsPregenerated() {
|
| + // These variants are compiled ahead of time. See next method.
|
| + if (the_int_.is(r1) && the_heap_number_.is(r0) && scratch_.is(r2)) {
|
| + return true;
|
| + }
|
| + if (the_int_.is(r2) && the_heap_number_.is(r0) && scratch_.is(r3)) {
|
| + 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(r1, r0, r2);
|
| + WriteInt32ToHeapNumberStub stub2(r2, r0, r3);
|
| + stub1.GetCode()->set_is_pregenerated(true);
|
| + stub2.GetCode()->set_is_pregenerated(true);
|
| +}
|
| +
|
| +
|
| // See comment for class.
|
| void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) {
|
| Label max_negative_int;
|
| @@ -1197,6 +1222,8 @@
|
| __ vmov(d0, r0, r1);
|
| __ vmov(d1, r2, r3);
|
| }
|
| +
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| __ CallCFunction(ExternalReference::compare_doubles(masm->isolate()),
|
| 0, 2);
|
| __ pop(pc); // Return.
|
| @@ -1214,7 +1241,7 @@
|
| // If either operand is a JS object or an oddball value, then they are
|
| // not equal since their pointers are different.
|
| // There is no test for undetectability in strict equality.
|
| - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE);
|
| + STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
|
| Label first_non_object;
|
| // Get the type of the first operand into r2 and compare it with
|
| // FIRST_SPEC_OBJECT_TYPE.
|
| @@ -1606,6 +1633,8 @@
|
| // The stub expects its argument in the tos_ register and returns its result in
|
| // it, too: zero for false, and a non-zero value for true.
|
| void ToBooleanStub::Generate(MacroAssembler* masm) {
|
| + // This stub overrides SometimesSetsUpAFrame() to return false. That means
|
| + // we cannot call anything that could cause a GC from this stub.
|
| // This stub uses VFP3 instructions.
|
| CpuFeatures::Scope scope(VFP3);
|
|
|
| @@ -1713,6 +1742,41 @@
|
| }
|
|
|
|
|
| +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.
|
| + __ stm(db_w, sp, kCallerSaved | lr.bit());
|
| + if (save_doubles_ == kSaveFPRegs) {
|
| + CpuFeatures::Scope scope(VFP3);
|
| + __ sub(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters));
|
| + for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
|
| + DwVfpRegister reg = DwVfpRegister::from_code(i);
|
| + __ vstr(reg, MemOperand(sp, i * kDoubleSize));
|
| + }
|
| + }
|
| + const int argument_count = 1;
|
| + const int fp_argument_count = 0;
|
| + const Register scratch = r1;
|
| +
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ PrepareCallCFunction(argument_count, fp_argument_count, scratch);
|
| + __ mov(r0, Operand(ExternalReference::isolate_address()));
|
| + __ CallCFunction(
|
| + ExternalReference::store_buffer_overflow_function(masm->isolate()),
|
| + argument_count);
|
| + if (save_doubles_ == kSaveFPRegs) {
|
| + CpuFeatures::Scope scope(VFP3);
|
| + for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) {
|
| + DwVfpRegister reg = DwVfpRegister::from_code(i);
|
| + __ vldr(reg, MemOperand(sp, i * kDoubleSize));
|
| + }
|
| + __ add(sp, sp, Operand(kDoubleSize * DwVfpRegister::kNumRegisters));
|
| + }
|
| + __ ldm(ia_w, sp, kCallerSaved | pc.bit()); // Also pop pc to get Ret(0).
|
| +}
|
| +
|
| +
|
| void UnaryOpStub::PrintName(StringStream* stream) {
|
| const char* op_name = Token::Name(op_);
|
| const char* overwrite_name = NULL; // Make g++ happy.
|
| @@ -1866,12 +1930,13 @@
|
| __ jmp(&heapnumber_allocated);
|
|
|
| __ bind(&slow_allocate_heapnumber);
|
| - __ EnterInternalFrame();
|
| - __ push(r0);
|
| - __ CallRuntime(Runtime::kNumberAlloc, 0);
|
| - __ mov(r1, Operand(r0));
|
| - __ pop(r0);
|
| - __ LeaveInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ push(r0);
|
| + __ CallRuntime(Runtime::kNumberAlloc, 0);
|
| + __ mov(r1, Operand(r0));
|
| + __ pop(r0);
|
| + }
|
|
|
| __ bind(&heapnumber_allocated);
|
| __ ldr(r3, FieldMemOperand(r0, HeapNumber::kMantissaOffset));
|
| @@ -1912,13 +1977,14 @@
|
| __ jmp(&heapnumber_allocated);
|
|
|
| __ bind(&slow_allocate_heapnumber);
|
| - __ EnterInternalFrame();
|
| - __ push(r0); // Push the heap number, not the untagged int32.
|
| - __ CallRuntime(Runtime::kNumberAlloc, 0);
|
| - __ mov(r2, r0); // Move the new heap number into r2.
|
| - // Get the heap number into r0, now that the new heap number is in r2.
|
| - __ pop(r0);
|
| - __ LeaveInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ push(r0); // Push the heap number, not the untagged int32.
|
| + __ CallRuntime(Runtime::kNumberAlloc, 0);
|
| + __ mov(r2, r0); // Move the new heap number into r2.
|
| + // Get the heap number into r0, now that the new heap number is in r2.
|
| + __ pop(r0);
|
| + }
|
|
|
| // Convert the heap number in r0 to an untagged integer in r1.
|
| // This can't go slow-case because it's the same number we already
|
| @@ -2028,6 +2094,10 @@
|
|
|
|
|
| void BinaryOpStub::Generate(MacroAssembler* masm) {
|
| + // Explicitly allow generation of nested stubs. It is safe here because
|
| + // generation code does not use any raw pointers.
|
| + AllowStubCallsScope allow_stub_calls(masm, true);
|
| +
|
| switch (operands_type_) {
|
| case BinaryOpIC::UNINITIALIZED:
|
| GenerateTypeTransition(masm);
|
| @@ -3133,10 +3203,11 @@
|
| __ LoadRoot(r5, Heap::kHeapNumberMapRootIndex);
|
| __ AllocateHeapNumber(r0, scratch0, scratch1, r5, &skip_cache);
|
| __ vstr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
|
| - __ EnterInternalFrame();
|
| - __ push(r0);
|
| - __ CallRuntime(RuntimeFunction(), 1);
|
| - __ LeaveInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ push(r0);
|
| + __ CallRuntime(RuntimeFunction(), 1);
|
| + }
|
| __ vldr(d2, FieldMemOperand(r0, HeapNumber::kValueOffset));
|
| __ Ret();
|
|
|
| @@ -3149,14 +3220,15 @@
|
|
|
| // We return the value in d2 without adding it to the cache, but
|
| // we cause a scavenging GC so that future allocations will succeed.
|
| - __ EnterInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
|
|
| - // Allocate an aligned object larger than a HeapNumber.
|
| - ASSERT(4 * kPointerSize >= HeapNumber::kSize);
|
| - __ mov(scratch0, Operand(4 * kPointerSize));
|
| - __ push(scratch0);
|
| - __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
|
| - __ LeaveInternalFrame();
|
| + // Allocate an aligned object larger than a HeapNumber.
|
| + ASSERT(4 * kPointerSize >= HeapNumber::kSize);
|
| + __ mov(scratch0, Operand(4 * kPointerSize));
|
| + __ push(scratch0);
|
| + __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
|
| + }
|
| __ Ret();
|
| }
|
| }
|
| @@ -3173,6 +3245,7 @@
|
| } else {
|
| __ vmov(r0, r1, d2);
|
| }
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| switch (type_) {
|
| case TranscendentalCache::SIN:
|
| __ CallCFunction(ExternalReference::math_sin_double_function(isolate),
|
| @@ -3268,11 +3341,14 @@
|
| __ push(lr);
|
| __ PrepareCallCFunction(1, 1, scratch);
|
| __ SetCallCDoubleArguments(double_base, exponent);
|
| - __ CallCFunction(
|
| - ExternalReference::power_double_int_function(masm->isolate()),
|
| - 1, 1);
|
| - __ pop(lr);
|
| - __ GetCFunctionDoubleResult(double_result);
|
| + {
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ CallCFunction(
|
| + ExternalReference::power_double_int_function(masm->isolate()),
|
| + 1, 1);
|
| + __ pop(lr);
|
| + __ GetCFunctionDoubleResult(double_result);
|
| + }
|
| __ vstr(double_result,
|
| FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
|
| __ mov(r0, heapnumber);
|
| @@ -3298,11 +3374,14 @@
|
| __ push(lr);
|
| __ PrepareCallCFunction(0, 2, scratch);
|
| __ SetCallCDoubleArguments(double_base, double_exponent);
|
| - __ CallCFunction(
|
| - ExternalReference::power_double_double_function(masm->isolate()),
|
| - 0, 2);
|
| - __ pop(lr);
|
| - __ GetCFunctionDoubleResult(double_result);
|
| + {
|
| + AllowExternalCallThatCantCauseGC scope(masm);
|
| + __ CallCFunction(
|
| + ExternalReference::power_double_double_function(masm->isolate()),
|
| + 0, 2);
|
| + __ pop(lr);
|
| + __ GetCFunctionDoubleResult(double_result);
|
| + }
|
| __ vstr(double_result,
|
| FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
|
| __ mov(r0, heapnumber);
|
| @@ -3319,6 +3398,37 @@
|
| }
|
|
|
|
|
| +bool CEntryStub::IsPregenerated() {
|
| + return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
|
| + result_size_ == 1;
|
| +}
|
| +
|
| +
|
| +void CodeStub::GenerateStubsAheadOfTime() {
|
| + CEntryStub::GenerateAheadOfTime();
|
| + WriteInt32ToHeapNumberStub::GenerateFixedRegStubsAheadOfTime();
|
| + StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime();
|
| + RecordWriteStub::GenerateFixedRegStubsAheadOfTime();
|
| +}
|
| +
|
| +
|
| +void CodeStub::GenerateFPStubs() {
|
| + CEntryStub save_doubles(1, kSaveFPRegs);
|
| + Handle<Code> code = save_doubles.GetCode();
|
| + code->set_is_pregenerated(true);
|
| + StoreBufferOverflowStub stub(kSaveFPRegs);
|
| + stub.GetCode()->set_is_pregenerated(true);
|
| + code->GetIsolate()->set_fp_stubs_generated(true);
|
| +}
|
| +
|
| +
|
| +void CEntryStub::GenerateAheadOfTime() {
|
| + CEntryStub stub(1, kDontSaveFPRegs);
|
| + Handle<Code> code = stub.GetCode();
|
| + code->set_is_pregenerated(true);
|
| +}
|
| +
|
| +
|
| void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
| __ Throw(r0);
|
| }
|
| @@ -3430,8 +3540,7 @@
|
| __ b(eq, throw_out_of_memory_exception);
|
|
|
| // Retrieve the pending exception and clear the variable.
|
| - __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate)));
|
| - __ ldr(r3, MemOperand(ip));
|
| + __ mov(r3, Operand(isolate->factory()->the_hole_value()));
|
| __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
| isolate)));
|
| __ ldr(r0, MemOperand(ip));
|
| @@ -3469,6 +3578,7 @@
|
| __ sub(r6, r6, Operand(kPointerSize));
|
|
|
| // Enter the exit frame that transitions from JavaScript to C++.
|
| + FrameScope scope(masm, StackFrame::MANUAL);
|
| __ EnterExitFrame(save_doubles_);
|
|
|
| // Setup argc and the builtin function in callee-saved registers.
|
| @@ -3613,8 +3723,7 @@
|
| // saved values before returning a failure to C.
|
|
|
| // Clear any pending exceptions.
|
| - __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate)));
|
| - __ ldr(r5, MemOperand(ip));
|
| + __ mov(r5, Operand(isolate->factory()->the_hole_value()));
|
| __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
| isolate)));
|
| __ str(r5, MemOperand(ip));
|
| @@ -3851,10 +3960,11 @@
|
| }
|
| __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
|
| } else {
|
| - __ EnterInternalFrame();
|
| - __ Push(r0, r1);
|
| - __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
|
| - __ LeaveInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ Push(r0, r1);
|
| + __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
|
| + }
|
| __ cmp(r0, Operand::Zero());
|
| __ LoadRoot(r0, Heap::kTrueValueRootIndex, eq);
|
| __ LoadRoot(r0, Heap::kFalseValueRootIndex, ne);
|
| @@ -4480,8 +4590,7 @@
|
|
|
| // For arguments 4 and 3 get string length, calculate start of string data and
|
| // calculate the shift of the index (0 for ASCII and 1 for two byte).
|
| - STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
| - __ add(r8, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + __ add(r8, subject, Operand(SeqString::kHeaderSize - kHeapObjectTag));
|
| __ eor(r3, r3, Operand(1));
|
| // Load the length from the original subject string from the previous stack
|
| // frame. Therefore we have to use fp, which points exactly to two pointer
|
| @@ -4532,8 +4641,7 @@
|
| // stack overflow (on the backtrack stack) was detected in RegExp code but
|
| // haven't created the exception yet. Handle that in the runtime system.
|
| // TODO(592): Rerunning the RegExp to get the stack overflow exception.
|
| - __ mov(r1, Operand(ExternalReference::the_hole_value_location(isolate)));
|
| - __ ldr(r1, MemOperand(r1, 0));
|
| + __ mov(r1, Operand(isolate->factory()->the_hole_value()));
|
| __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
| isolate)));
|
| __ ldr(r0, MemOperand(r2, 0));
|
| @@ -4575,16 +4683,25 @@
|
| __ str(r2, FieldMemOperand(last_match_info_elements,
|
| RegExpImpl::kLastCaptureCountOffset));
|
| // Store last subject and last input.
|
| - __ mov(r3, last_match_info_elements); // Moved up to reduce latency.
|
| __ str(subject,
|
| FieldMemOperand(last_match_info_elements,
|
| RegExpImpl::kLastSubjectOffset));
|
| - __ RecordWrite(r3, Operand(RegExpImpl::kLastSubjectOffset), r2, r7);
|
| + __ mov(r2, subject);
|
| + __ RecordWriteField(last_match_info_elements,
|
| + RegExpImpl::kLastSubjectOffset,
|
| + r2,
|
| + r7,
|
| + kLRHasNotBeenSaved,
|
| + kDontSaveFPRegs);
|
| __ str(subject,
|
| FieldMemOperand(last_match_info_elements,
|
| RegExpImpl::kLastInputOffset));
|
| - __ mov(r3, last_match_info_elements);
|
| - __ RecordWrite(r3, Operand(RegExpImpl::kLastInputOffset), r2, r7);
|
| + __ RecordWriteField(last_match_info_elements,
|
| + RegExpImpl::kLastInputOffset,
|
| + subject,
|
| + r7,
|
| + kLRHasNotBeenSaved,
|
| + kDontSaveFPRegs);
|
|
|
| // Get the static offsets vector filled by the native regexp code.
|
| ExternalReference address_of_static_offsets_vector =
|
| @@ -4712,6 +4829,22 @@
|
| }
|
|
|
|
|
| +void CallFunctionStub::FinishCode(Code* code) {
|
| + code->set_has_function_cache(false);
|
| +}
|
| +
|
| +
|
| +void CallFunctionStub::Clear(Heap* heap, Address address) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +Object* CallFunctionStub::GetCachedValue(Address address) {
|
| + UNREACHABLE();
|
| + return NULL;
|
| +}
|
| +
|
| +
|
| void CallFunctionStub::Generate(MacroAssembler* masm) {
|
| Label slow, non_function;
|
|
|
| @@ -6425,12 +6558,13 @@
|
| // Call the runtime system in a fresh internal frame.
|
| ExternalReference miss =
|
| ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate());
|
| - __ EnterInternalFrame();
|
| - __ Push(r1, r0);
|
| - __ mov(ip, Operand(Smi::FromInt(op_)));
|
| - __ push(ip);
|
| - __ CallExternalReference(miss, 3);
|
| - __ LeaveInternalFrame();
|
| + {
|
| + FrameScope scope(masm, StackFrame::INTERNAL);
|
| + __ Push(r1, r0);
|
| + __ mov(ip, Operand(Smi::FromInt(op_)));
|
| + __ push(ip);
|
| + __ CallExternalReference(miss, 3);
|
| + }
|
| // Compute the entry point of the rewritten stub.
|
| __ add(r2, r0, Operand(Code::kHeaderSize - kHeapObjectTag));
|
| // Restore registers.
|
| @@ -6613,6 +6747,8 @@
|
|
|
|
|
| void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
|
| + // This stub overrides SometimesSetsUpAFrame() to return false. That means
|
| + // we cannot call anything that could cause a GC from this stub.
|
| // Registers:
|
| // result: StringDictionary to probe
|
| // r1: key
|
| @@ -6702,6 +6838,267 @@
|
| }
|
|
|
|
|
| +struct AheadOfTimeWriteBarrierStubList {
|
| + Register object, value, address;
|
| + RememberedSetAction action;
|
| +};
|
| +
|
| +
|
| +struct AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
|
| + // Used in RegExpExecStub.
|
| + { r6, r4, r7, EMIT_REMEMBERED_SET },
|
| + { r6, r2, r7, EMIT_REMEMBERED_SET },
|
| + // Used in CompileArrayPushCall.
|
| + // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore.
|
| + // Also used in KeyedStoreIC::GenerateGeneric.
|
| + { r3, r4, r5, EMIT_REMEMBERED_SET },
|
| + // Used in CompileStoreGlobal.
|
| + { r4, r1, r2, OMIT_REMEMBERED_SET },
|
| + // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField.
|
| + { r1, r2, r3, EMIT_REMEMBERED_SET },
|
| + { r3, r2, r1, EMIT_REMEMBERED_SET },
|
| + // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
|
| + { r2, r1, r3, EMIT_REMEMBERED_SET },
|
| + { r3, r1, r2, EMIT_REMEMBERED_SET },
|
| + // KeyedStoreStubCompiler::GenerateStoreFastElement.
|
| + { r4, r2, r3, EMIT_REMEMBERED_SET },
|
| + // Null termination.
|
| + { no_reg, no_reg, no_reg, EMIT_REMEMBERED_SET}
|
| +};
|
| +
|
| +
|
| +bool RecordWriteStub::IsPregenerated() {
|
| + 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 false;
|
| +}
|
| +
|
| +
|
| +bool StoreBufferOverflowStub::IsPregenerated() {
|
| + return save_doubles_ == kDontSaveFPRegs || ISOLATE->fp_stubs_generated();
|
| +}
|
| +
|
| +
|
| +void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() {
|
| + StoreBufferOverflowStub stub1(kDontSaveFPRegs);
|
| + stub1.GetCode()->set_is_pregenerated(true);
|
| +}
|
| +
|
| +
|
| +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()->set_is_pregenerated(true);
|
| + }
|
| +}
|
| +
|
| +
|
| +// 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 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 compare instructions (a nop in this position) and the
|
| + // real branch when we start and stop incremental heap marking.
|
| + // See RecordWriteStub::Patch for details.
|
| + __ b(&skip_to_incremental_noncompacting);
|
| + __ b(&skip_to_incremental_compacting);
|
| +
|
| + if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
|
| + __ RememberedSetHelper(object_,
|
| + 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.
|
| + ASSERT(Assembler::GetBranchOffset(masm->instr_at(0)) < (1 << 12));
|
| + ASSERT(Assembler::GetBranchOffset(masm->instr_at(4)) < (1 << 12));
|
| + PatchBranchIntoNop(masm, 0);
|
| + PatchBranchIntoNop(masm, Assembler::kInstrSize);
|
| +}
|
| +
|
| +
|
| +void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
|
| + regs_.Save(masm);
|
| +
|
| + if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
|
| + Label dont_need_remembered_set;
|
| +
|
| + __ ldr(regs_.scratch0(), MemOperand(regs_.address(), 0));
|
| + __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
|
| + 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(object_,
|
| + 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 =
|
| + r0.is(regs_.address()) ? regs_.scratch0() : regs_.address();
|
| + ASSERT(!address.is(regs_.object()));
|
| + ASSERT(!address.is(r0));
|
| + __ Move(address, regs_.address());
|
| + __ Move(r0, regs_.object());
|
| + if (mode == INCREMENTAL_COMPACTION) {
|
| + __ Move(r1, address);
|
| + } else {
|
| + ASSERT(mode == INCREMENTAL);
|
| + __ ldr(r1, MemOperand(address, 0));
|
| + }
|
| + __ mov(r2, 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(object_,
|
| + address_,
|
| + value_,
|
| + save_fp_regs_mode_,
|
| + MacroAssembler::kReturnAtEnd);
|
| + } else {
|
| + __ Ret();
|
| + }
|
| +
|
| + __ bind(&on_black);
|
| +
|
| + // Get the value from the slot.
|
| + __ ldr(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(object_,
|
| + 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
|
|
|