diff options
Diffstat (limited to 'deps/v8/src/arm/macro-assembler-arm.cc')
-rw-r--r-- | deps/v8/src/arm/macro-assembler-arm.cc | 566 |
1 files changed, 474 insertions, 92 deletions
diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index f37f310218..b274b2fe42 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -42,7 +42,8 @@ namespace internal { MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size) : Assembler(arg_isolate, buffer, size), generating_stub_(false), - allow_stub_calls_(true) { + allow_stub_calls_(true), + has_frame_(false) { if (isolate() != NULL) { code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), isolate()); @@ -406,32 +407,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. - Bfc(object, 0, kPageSizeBits); - - // Calculate region number. - Ubfx(address, address, Page::kRegionSizeLog2, - kPageSizeBits - Page::kRegionSizeLog2); - - // Mark region dirty. - ldr(scratch, MemOperand(object, Page::kDirtyFlagOffset)); - mov(ip, Operand(1)); - orr(scratch, scratch, Operand(ip, LSL, address)); - str(scratch, MemOperand(object, Page::kDirtyFlagOffset)); -} - - void MacroAssembler::InNewSpace(Register object, Register scratch, Condition cond, @@ -443,38 +418,52 @@ void MacroAssembler::InNewSpace(Register object, } -// Will clobber 4 registers: object, offset, scratch, ip. 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, + LinkRegisterStatus lr_status, + SaveFPRegsMode save_fp, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { + // 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. - add(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); + add(dst, object, Operand(offset - kHeapObjectTag)); + if (emit_debug_code()) { + Label ok; + tst(dst, Operand((1 << kPointerSizeLog2) - 1)); + b(eq, &ok); + stop("Unaligned cell in write barrier"); + bind(&ok); + } + + RecordWrite(object, + dst, + value, + lr_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()) { - mov(object, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch0, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch1, Operand(BitCast<int32_t>(kZapValue))); + mov(value, Operand(BitCast<int32_t>(kZapValue + 4))); + mov(dst, Operand(BitCast<int32_t>(kZapValue + 8))); } } @@ -484,29 +473,94 @@ void MacroAssembler::RecordWrite(Register object, // tag is shifted away. void MacroAssembler::RecordWrite(Register object, Register address, - Register scratch) { + Register value, + LinkRegisterStatus lr_status, + SaveFPRegsMode fp_mode, + RememberedSetAction remembered_set_action, + SmiCheck smi_check) { // 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); + tst(value, Operand(kSmiTagMask)); + b(eq, &done); + } + + 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 (lr_status == kLRHasNotBeenSaved) { + push(lr); + } + RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode); + CallStub(&stub); + if (lr_status == kLRHasNotBeenSaved) { + pop(lr); + } 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()) { - mov(object, Operand(BitCast<int32_t>(kZapValue))); - mov(address, Operand(BitCast<int32_t>(kZapValue))); - mov(scratch, Operand(BitCast<int32_t>(kZapValue))); + mov(address, Operand(BitCast<int32_t>(kZapValue + 12))); + mov(value, Operand(BitCast<int32_t>(kZapValue + 16))); + } +} + + +void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. + Register address, + Register scratch, + SaveFPRegsMode fp_mode, + RememberedSetFinalAction and_then) { + Label done; + if (FLAG_debug_code) { + Label ok; + JumpIfNotInNewSpace(object, scratch, &ok); + stop("Remembered set pointer is in new space"); + bind(&ok); + } + // Load store buffer top. + ExternalReference store_buffer = + ExternalReference::store_buffer_top(isolate()); + mov(ip, Operand(store_buffer)); + ldr(scratch, MemOperand(ip)); + // Store pointer to buffer and increment buffer top. + str(address, MemOperand(scratch, kPointerSize, PostIndex)); + // Write back new top of buffer. + str(scratch, MemOperand(ip)); + // Call stub on end of buffer. + // Check for end of buffer. + tst(scratch, Operand(StoreBuffer::kStoreBufferOverflowBit)); + if (and_then == kFallThroughAtEnd) { + b(eq, &done); + } else { + ASSERT(and_then == kReturnAtEnd); + Ret(ne); + } + push(lr); + StoreBufferOverflowStub store_buffer_overflow = + StoreBufferOverflowStub(fp_mode); + CallStub(&store_buffer_overflow); + pop(lr); + bind(&done); + if (and_then == kReturnAtEnd) { + Ret(); } } @@ -961,6 +1015,9 @@ void MacroAssembler::InvokeCode(Register code, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag, @@ -988,6 +1045,9 @@ void MacroAssembler::InvokeCode(Handle<Code> code, RelocInfo::Mode rmode, InvokeFlag flag, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + Label done; InvokePrologue(expected, actual, code, no_reg, &done, flag, @@ -1011,6 +1071,9 @@ void MacroAssembler::InvokeFunction(Register fun, InvokeFlag flag, const CallWrapper& call_wrapper, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + // Contract with called JS functions requires that function is passed in r1. ASSERT(fun.is(r1)); @@ -1035,6 +1098,9 @@ void MacroAssembler::InvokeFunction(JSFunction* function, const ParameterCount& actual, InvokeFlag flag, CallKind call_kind) { + // You can't call a function without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + ASSERT(function->is_compiled()); // Get the function and setup the context. @@ -1090,10 +1156,10 @@ void MacroAssembler::IsObjectJSStringType(Register object, #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::DebugBreak() { - ASSERT(allow_stub_calls()); mov(r0, Operand(0, RelocInfo::NONE)); mov(r1, Operand(ExternalReference(Runtime::kDebugBreak, isolate()))); CEntryStub ces(1); + ASSERT(AllowThisStubCall(&ces)); Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); } #endif @@ -1793,13 +1859,127 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); b(hi, fail); } +void MacroAssembler::CheckFastObjectElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 1); + ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + b(ls, fail); + cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + b(hi, fail); +} + + +void MacroAssembler::CheckFastSmiOnlyElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); + ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); + cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + b(hi, fail); +} + + +void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, + Register key_reg, + Register receiver_reg, + Register elements_reg, + Register scratch1, + Register scratch2, + Register scratch3, + Register scratch4, + Label* fail) { + Label smi_value, maybe_nan, have_double_value, is_nan, done; + Register mantissa_reg = scratch2; + Register exponent_reg = scratch3; + + // Handle smi values specially. + JumpIfSmi(value_reg, &smi_value); + + // Ensure that the object is a heap number + CheckMap(value_reg, + scratch1, + isolate()->factory()->heap_number_map(), + fail, + DONT_DO_SMI_CHECK); + + // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000 + // in the exponent. + mov(scratch1, Operand(kNaNOrInfinityLowerBoundUpper32)); + ldr(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset)); + cmp(exponent_reg, scratch1); + b(ge, &maybe_nan); + + ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + + bind(&have_double_value); + add(scratch1, elements_reg, + Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); + str(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize)); + uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); + str(exponent_reg, FieldMemOperand(scratch1, offset)); + jmp(&done); + + bind(&maybe_nan); + // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise + // it's an Infinity, and the non-NaN code path applies. + b(gt, &is_nan); + ldr(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset)); + cmp(mantissa_reg, Operand(0)); + b(eq, &have_double_value); + bind(&is_nan); + // Load canonical NaN for storing into the double array. + uint64_t nan_int64 = BitCast<uint64_t>( + FixedDoubleArray::canonical_not_the_hole_nan_as_double()); + mov(mantissa_reg, Operand(static_cast<uint32_t>(nan_int64))); + mov(exponent_reg, Operand(static_cast<uint32_t>(nan_int64 >> 32))); + jmp(&have_double_value); + + bind(&smi_value); + add(scratch1, elements_reg, + Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag)); + add(scratch1, scratch1, + Operand(key_reg, LSL, kDoubleSizeLog2 - kSmiTagSize)); + // scratch1 is now effective address of the double element + + FloatingPointHelper::Destination destination; + if (CpuFeatures::IsSupported(VFP3)) { + destination = FloatingPointHelper::kVFPRegisters; + } else { + destination = FloatingPointHelper::kCoreRegisters; + } + + Register untagged_value = receiver_reg; + SmiUntag(untagged_value, value_reg); + FloatingPointHelper::ConvertIntToDouble(this, + untagged_value, + destination, + d0, + mantissa_reg, + exponent_reg, + scratch4, + s2); + if (destination == FloatingPointHelper::kVFPRegisters) { + CpuFeatures::Scope scope(VFP3); + vstr(d0, scratch1, 0); + } else { + str(mantissa_reg, MemOperand(scratch1, 0)); + str(exponent_reg, MemOperand(scratch1, Register::kSizeInBytes)); + } + bind(&done); +} + + void MacroAssembler::CheckMap(Register obj, Register scratch, Handle<Map> map, @@ -1895,13 +2075,13 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, void MacroAssembler::CallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. Call(stub->GetCode(), RelocInfo::CODE_TARGET, kNoASTId, cond); } MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. Object* result; { MaybeObject* maybe_result = stub->TryGetCode(); if (!maybe_result->ToObject(&result)) return maybe_result; @@ -1913,13 +2093,12 @@ MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond) { void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. + ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe()); Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond); } MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub, Condition cond) { - ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs. Object* result; { MaybeObject* maybe_result = stub->TryGetCode(); if (!maybe_result->ToObject(&result)) return maybe_result; @@ -2022,6 +2201,12 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( } +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { + if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false; + return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe(); +} + + void MacroAssembler::IllegalOperation(int num_arguments) { if (num_arguments > 0) { add(sp, sp, Operand(num_arguments * kPointerSize)); @@ -2417,8 +2602,7 @@ void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) { const Runtime::Function* function = Runtime::FunctionForId(id); mov(r0, Operand(function->nargs)); mov(r1, Operand(ExternalReference(function, isolate()))); - CEntryStub stub(1); - stub.SaveDoubles(); + CEntryStub stub(1, kSaveFPRegs); CallStub(&stub); } @@ -2491,6 +2675,9 @@ MaybeObject* MacroAssembler::TryJumpToExternalReference( void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, const CallWrapper& call_wrapper) { + // You can't call a builtin without a valid frame. + ASSERT(flag == JUMP_FUNCTION || has_frame()); + GetBuiltinEntry(r2, id); if (flag == CALL_FUNCTION) { call_wrapper.BeforeCall(CallSize(r2)); @@ -2622,14 +2809,20 @@ void MacroAssembler::Abort(const char* msg) { RecordComment(msg); } #endif - // Disable stub call restrictions to always allow calls to abort. - AllowStubCallsScope allow_scope(this, true); mov(r0, Operand(p0)); push(r0); mov(r0, Operand(Smi::FromInt(p1 - p0))); push(r0); - CallRuntime(Runtime::kAbort, 2); + // Disable stub call restrictions to always allow calls to abort. + if (!has_frame_) { + // We don't actually want to generate a pile of code for this, so just + // claim there is a stack frame, without generating one. + FrameScope scope(this, StackFrame::NONE); + CallRuntime(Runtime::kAbort, 2); + } else { + CallRuntime(Runtime::kAbort, 2); + } // will not return here if (is_const_pool_blocked()) { // If the calling code cares about the exact number of @@ -2930,6 +3123,19 @@ void MacroAssembler::CopyBytes(Register src, } +void MacroAssembler::InitializeFieldsWithFiller(Register start_offset, + Register end_offset, + Register filler) { + Label loop, entry; + b(&entry); + bind(&loop); + str(filler, MemOperand(start_offset, kPointerSize, PostIndex)); + bind(&entry); + cmp(start_offset, end_offset); + b(lt, &loop); +} + + void MacroAssembler::CountLeadingZeros(Register zeros, // Answer. Register source, // Input. Register scratch) { @@ -3089,23 +3295,15 @@ void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg, void MacroAssembler::CallCFunction(ExternalReference function, int num_reg_arguments, int num_double_arguments) { - CallCFunctionHelper(no_reg, - function, - ip, - num_reg_arguments, - num_double_arguments); + mov(ip, Operand(function)); + CallCFunctionHelper(ip, num_reg_arguments, num_double_arguments); } void MacroAssembler::CallCFunction(Register function, - Register scratch, - int num_reg_arguments, - int num_double_arguments) { - CallCFunctionHelper(function, - ExternalReference::the_hole_value_location(isolate()), - scratch, - num_reg_arguments, - num_double_arguments); + int num_reg_arguments, + int num_double_arguments) { + CallCFunctionHelper(function, num_reg_arguments, num_double_arguments); } @@ -3116,17 +3314,15 @@ void MacroAssembler::CallCFunction(ExternalReference function, void MacroAssembler::CallCFunction(Register function, - Register scratch, int num_arguments) { - CallCFunction(function, scratch, num_arguments, 0); + CallCFunction(function, num_arguments, 0); } void MacroAssembler::CallCFunctionHelper(Register function, - ExternalReference function_reference, - Register scratch, int num_reg_arguments, int num_double_arguments) { + ASSERT(has_frame()); // Make sure that the stack is aligned before calling a C function unless // running in the simulator. The simulator has its own alignment check which // provides more information. @@ -3150,10 +3346,6 @@ void MacroAssembler::CallCFunctionHelper(Register function, // Just call directly. The function called cannot cause a GC, or // allow preemption, so the return address in the link register // stays correct. - if (function.is(no_reg)) { - mov(scratch, Operand(function_reference)); - function = scratch; - } Call(function); int stack_passed_arguments = CalculateStackPassedWords( num_reg_arguments, num_double_arguments); @@ -3185,6 +3377,185 @@ void MacroAssembler::GetRelocatedValueLocation(Register ldr_location, } +void MacroAssembler::CheckPageFlag( + Register object, + Register scratch, + int mask, + Condition cc, + Label* condition_met) { + and_(scratch, object, Operand(~Page::kPageAlignmentMask)); + ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); + tst(scratch, Operand(mask)); + b(cc, condition_met); +} + + +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, no_reg)); + + GetMarkBits(object, bitmap_scratch, mask_scratch); + + Label other_color, word_boundary; + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + tst(ip, Operand(mask_scratch)); + b(first_bit == 1 ? eq : ne, &other_color); + // Shift left 1 by adding. + add(mask_scratch, mask_scratch, Operand(mask_scratch), SetCC); + b(eq, &word_boundary); + tst(ip, Operand(mask_scratch)); + b(second_bit == 1 ? ne : eq, has_color); + jmp(&other_color); + + bind(&word_boundary); + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize + kPointerSize)); + tst(ip, Operand(1)); + b(second_bit == 1 ? ne : eq, has_color); + 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) { + Label is_data_object; + ldr(scratch, FieldMemOperand(value, HeapObject::kMapOffset)); + CompareRoot(scratch, Heap::kHeapNumberMapRootIndex); + b(eq, &is_data_object); + 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. + ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); + tst(scratch, Operand(kIsIndirectStringMask | kIsNotStringMask)); + b(ne, not_data_object); + 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)); + Ubfx(mask_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); + const int kLowBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; + Ubfx(ip, addr_reg, kLowBits, kPageSizeBits - kLowBits); + add(bitmap_reg, bitmap_reg, Operand(ip, LSL, kPointerSizeLog2)); + mov(ip, Operand(1)); + mov(mask_reg, Operand(ip, LSL, 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, ip)); + 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. + ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + tst(mask_scratch, load_scratch); + b(ne, &done); + + if (FLAG_debug_code) { + // Check for impossible bit pattern. + Label ok; + // LSL may overflow, making the check conservative. + tst(load_scratch, Operand(mask_scratch, LSL, 1)); + b(eq, &ok); + 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 + ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); + CompareRoot(map, Heap::kHeapNumberMapRootIndex); + mov(length, Operand(HeapNumber::kSize), LeaveCC, eq); + b(eq, &is_data_object); + + // 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; + ldrb(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); + tst(instance_type, Operand(kIsIndirectStringMask | kIsNotStringMask)); + b(ne, value_is_white_and_not_data); + // 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); + tst(instance_type, Operand(kExternalStringTag)); + mov(length, Operand(ExternalString::kSize), LeaveCC, ne); + b(ne, &is_data_object); + + // 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); + ldr(ip, FieldMemOperand(value, String::kLengthOffset)); + tst(instance_type, Operand(kStringEncodingMask)); + mov(ip, Operand(ip, LSR, 1), LeaveCC, ne); + add(length, ip, 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. + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + orr(ip, ip, Operand(mask_scratch)); + str(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); + + and_(bitmap_scratch, bitmap_scratch, Operand(~Page::kPageAlignmentMask)); + ldr(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + add(ip, ip, Operand(length)); + str(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); + + bind(&done); +} + + void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) { Usat(output_reg, 8, Operand(input_reg)); } @@ -3234,6 +3605,17 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +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), |