diff options
Diffstat (limited to 'src/3rdparty/v8/src/arm/macro-assembler-arm.cc')
-rw-r--r-- | src/3rdparty/v8/src/arm/macro-assembler-arm.cc | 490 |
1 files changed, 299 insertions, 191 deletions
diff --git a/src/3rdparty/v8/src/arm/macro-assembler-arm.cc b/src/3rdparty/v8/src/arm/macro-assembler-arm.cc index 4da2fec..dcc7149 100644 --- a/src/3rdparty/v8/src/arm/macro-assembler-arm.cc +++ b/src/3rdparty/v8/src/arm/macro-assembler-arm.cc @@ -108,7 +108,7 @@ void MacroAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode, int MacroAssembler::CallSize(Register target, Condition cond) { -#if USE_BLX +#ifdef USE_BLX return kInstrSize; #else return 2 * kInstrSize; @@ -121,7 +121,7 @@ void MacroAssembler::Call(Register target, Condition cond) { BlockConstPoolScope block_const_pool(this); Label start; bind(&start); -#if USE_BLX +#ifdef USE_BLX blx(target, cond); #else // set lr for return at current pc + 8 @@ -137,7 +137,19 @@ int MacroAssembler::CallSize( int size = 2 * kInstrSize; Instr mov_instr = cond | MOV | LeaveCC; intptr_t immediate = reinterpret_cast<intptr_t>(target); - if (!Operand(immediate, rmode).is_single_instruction(mov_instr)) { + if (!Operand(immediate, rmode).is_single_instruction(this, mov_instr)) { + size += kInstrSize; + } + return size; +} + + +int MacroAssembler::CallSizeNotPredictableCodeSize( + Address target, RelocInfo::Mode rmode, Condition cond) { + int size = 2 * kInstrSize; + Instr mov_instr = cond | MOV | LeaveCC; + intptr_t immediate = reinterpret_cast<intptr_t>(target); + if (!Operand(immediate, rmode).is_single_instruction(NULL, mov_instr)) { size += kInstrSize; } return size; @@ -146,15 +158,29 @@ int MacroAssembler::CallSize( void MacroAssembler::Call(Address target, RelocInfo::Mode rmode, - Condition cond) { + Condition cond, + TargetAddressStorageMode mode) { // Block constant pool for the call instruction sequence. BlockConstPoolScope block_const_pool(this); Label start; bind(&start); -#if USE_BLX - // On ARMv5 and after the recommended call sequence is: - // ldr ip, [pc, #...] - // blx ip + + bool old_predictable_code_size = predictable_code_size(); + if (mode == NEVER_INLINE_TARGET_ADDRESS) { + set_predictable_code_size(true); + } + +#ifdef USE_BLX + // Call sequence on V7 or later may be : + // movw ip, #... @ call address low 16 + // movt ip, #... @ call address high 16 + // blx ip + // @ return address + // Or for pre-V7 or values that may be back-patched + // to avoid ICache flushes: + // ldr ip, [pc, #...] @ call address + // blx ip + // @ return address // Statement positions are expected to be recorded when the target // address is loaded. The mov method will automatically record @@ -165,21 +191,22 @@ void MacroAssembler::Call(Address target, mov(ip, Operand(reinterpret_cast<int32_t>(target), rmode)); blx(ip, cond); - ASSERT(kCallTargetAddressOffset == 2 * kInstrSize); #else // Set lr for return at current pc + 8. mov(lr, Operand(pc), LeaveCC, cond); // Emit a ldr<cond> pc, [pc + offset of target in constant pool]. mov(pc, Operand(reinterpret_cast<int32_t>(target), rmode), LeaveCC, cond); - ASSERT(kCallTargetAddressOffset == kInstrSize); #endif ASSERT_EQ(CallSize(target, rmode, cond), SizeOfCodeGeneratedSince(&start)); + if (mode == NEVER_INLINE_TARGET_ADDRESS) { + set_predictable_code_size(old_predictable_code_size); + } } int MacroAssembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode, - unsigned ast_id, + TypeFeedbackId ast_id, Condition cond) { return CallSize(reinterpret_cast<Address>(code.location()), rmode, cond); } @@ -187,19 +214,18 @@ int MacroAssembler::CallSize(Handle<Code> code, void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode, - unsigned ast_id, - Condition cond) { + TypeFeedbackId ast_id, + Condition cond, + TargetAddressStorageMode mode) { Label start; bind(&start); ASSERT(RelocInfo::IsCodeTarget(rmode)); - if (rmode == RelocInfo::CODE_TARGET && ast_id != kNoASTId) { + if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) { SetRecordedAstId(ast_id); rmode = RelocInfo::CODE_TARGET_WITH_ID; } // 'code' is always generated ARM code, never THUMB code - Call(reinterpret_cast<Address>(code.location()), rmode, cond); - ASSERT_EQ(CallSize(code, rmode, ast_id, cond), - SizeOfCodeGeneratedSince(&start)); + Call(reinterpret_cast<Address>(code.location()), rmode, cond, mode); } @@ -265,8 +291,8 @@ void MacroAssembler::Move(Register dst, Register src, Condition cond) { void MacroAssembler::Move(DoubleRegister dst, DoubleRegister src) { - ASSERT(CpuFeatures::IsSupported(VFP3)); - CpuFeatures::Scope scope(VFP3); + ASSERT(CpuFeatures::IsSupported(VFP2)); + CpuFeatures::Scope scope(VFP2); if (!dst.is(src)) { vmov(dst, src); } @@ -276,17 +302,15 @@ void MacroAssembler::Move(DoubleRegister dst, DoubleRegister src) { void MacroAssembler::And(Register dst, Register src1, const Operand& src2, Condition cond) { if (!src2.is_reg() && - !src2.must_use_constant_pool() && + !src2.must_output_reloc_info(this) && src2.immediate() == 0) { mov(dst, Operand(0, RelocInfo::NONE), LeaveCC, cond); - - } else if (!src2.is_single_instruction() && - !src2.must_use_constant_pool() && + } else if (!src2.is_single_instruction(this) && + !src2.must_output_reloc_info(this) && CpuFeatures::IsSupported(ARMv7) && IsPowerOf2(src2.immediate() + 1)) { ubfx(dst, src1, 0, WhichPowerOf2(static_cast<uint32_t>(src2.immediate()) + 1), cond); - } else { and_(dst, src1, src2, LeaveCC, cond); } @@ -296,7 +320,7 @@ void MacroAssembler::And(Register dst, Register src1, const Operand& src2, void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width, Condition cond) { ASSERT(lsb < 32); - if (!CpuFeatures::IsSupported(ARMv7)) { + if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) { int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1); and_(dst, src1, Operand(mask), LeaveCC, cond); if (lsb != 0) { @@ -311,7 +335,7 @@ void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width, void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width, Condition cond) { ASSERT(lsb < 32); - if (!CpuFeatures::IsSupported(ARMv7)) { + if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) { int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1); and_(dst, src1, Operand(mask), LeaveCC, cond); int shift_up = 32 - lsb - width; @@ -339,7 +363,7 @@ void MacroAssembler::Bfi(Register dst, ASSERT(lsb + width < 32); ASSERT(!scratch.is(dst)); if (width == 0) return; - if (!CpuFeatures::IsSupported(ARMv7)) { + if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) { int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1); bic(dst, dst, Operand(mask)); and_(scratch, src, Operand((1 << width) - 1)); @@ -351,12 +375,14 @@ void MacroAssembler::Bfi(Register dst, } -void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) { +void MacroAssembler::Bfc(Register dst, Register src, int lsb, int width, + Condition cond) { ASSERT(lsb < 32); - if (!CpuFeatures::IsSupported(ARMv7)) { + if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) { int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1); - bic(dst, dst, Operand(mask)); + bic(dst, src, Operand(mask)); } else { + Move(dst, src, cond); bfc(dst, lsb, width, cond); } } @@ -364,7 +390,7 @@ void MacroAssembler::Bfc(Register dst, int lsb, int width, Condition cond) { void MacroAssembler::Usat(Register dst, int satpos, const Operand& src, Condition cond) { - if (!CpuFeatures::IsSupported(ARMv7)) { + if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) { ASSERT(!dst.is(pc) && !src.rm().is(pc)); ASSERT((satpos >= 0) && (satpos <= 31)); @@ -396,6 +422,16 @@ void MacroAssembler::Usat(Register dst, int satpos, const Operand& src, void MacroAssembler::LoadRoot(Register destination, Heap::RootListIndex index, Condition cond) { + if (CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS) && + !Heap::RootCanBeWrittenAfterInitialization(index)) { + Handle<Object> root(isolate()->heap()->roots_array_start()[index]); + if (!isolate()->heap()->InNewSpace(*root)) { + // The CPU supports fast immediate values, and this root will never + // change. We will load it as a relocatable immediate value. + mov(destination, Operand(root), LeaveCC, cond); + return; + } + } ldr(destination, MemOperand(kRootRegister, index << kPointerSizeLog2), cond); } @@ -672,7 +708,7 @@ void MacroAssembler::Ldrd(Register dst1, Register dst2, ASSERT((src.am() != PreIndex) && (src.am() != NegPreIndex)); // Generate two ldr instructions if ldrd is not available. - if (CpuFeatures::IsSupported(ARMv7)) { + if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size()) { CpuFeatures::Scope scope(ARMv7); ldrd(dst1, dst2, src, cond); } else { @@ -714,7 +750,7 @@ void MacroAssembler::Strd(Register src1, Register src2, ASSERT((dst.am() != PreIndex) && (dst.am() != NegPreIndex)); // Generate two str instructions if strd is not available. - if (CpuFeatures::IsSupported(ARMv7)) { + if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size()) { CpuFeatures::Scope scope(ARMv7); strd(src1, src2, dst, cond); } else { @@ -777,8 +813,9 @@ void MacroAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1, void MacroAssembler::Vmov(const DwVfpRegister dst, const double imm, + const Register scratch, const Condition cond) { - ASSERT(CpuFeatures::IsEnabled(VFP3)); + ASSERT(CpuFeatures::IsEnabled(VFP2)); static const DoubleRepresentation minus_zero(-0.0); static const DoubleRepresentation zero(0.0); DoubleRepresentation value(imm); @@ -788,7 +825,7 @@ void MacroAssembler::Vmov(const DwVfpRegister dst, } else if (value.bits == minus_zero.bits) { vneg(dst, kDoubleRegZero, cond); } else { - vmov(dst, imm, cond); + vmov(dst, imm, scratch, cond); } } @@ -930,6 +967,7 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles, } void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) { + ASSERT(CpuFeatures::IsSupported(VFP2)); if (use_eabi_hardfloat()) { Move(dst, d0); } else { @@ -1338,31 +1376,32 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, Check(ne, "we should not have an empty lexical context"); #endif - // Load the global context of the current context. - int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; + // Load the native context of the current context. + int offset = + Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; ldr(scratch, FieldMemOperand(scratch, offset)); - ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); + ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); - // Check the context is a global context. + // Check the context is a native context. if (emit_debug_code()) { // TODO(119): avoid push(holder_reg)/pop(holder_reg) // Cannot use ip as a temporary in this verification code. Due to the fact // that ip is clobbered as part of cmp with an object Operand. push(holder_reg); // Temporarily save holder on the stack. - // Read the first word and compare to the global_context_map. + // Read the first word and compare to the native_context_map. ldr(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset)); - LoadRoot(ip, Heap::kGlobalContextMapRootIndex); + LoadRoot(ip, Heap::kNativeContextMapRootIndex); cmp(holder_reg, ip); - Check(eq, "JSGlobalObject::global_context should be a global context."); + Check(eq, "JSGlobalObject::native_context should be a native context."); pop(holder_reg); // Restore holder. } // Check if both contexts are the same. - ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset)); + ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); cmp(scratch, Operand(ip)); b(eq, &same_contexts); - // Check the context is a global context. + // Check the context is a native context. if (emit_debug_code()) { // TODO(119): avoid push(holder_reg)/pop(holder_reg) // Cannot use ip as a temporary in this verification code. Due to the fact @@ -1374,13 +1413,13 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, Check(ne, "JSGlobalProxy::context() should not be null."); ldr(holder_reg, FieldMemOperand(holder_reg, HeapObject::kMapOffset)); - LoadRoot(ip, Heap::kGlobalContextMapRootIndex); + LoadRoot(ip, Heap::kNativeContextMapRootIndex); cmp(holder_reg, ip); - Check(eq, "JSGlobalObject::global_context should be a global context."); + Check(eq, "JSGlobalObject::native_context should be a native context."); // Restore ip is not needed. ip is reloaded below. pop(holder_reg); // Restore holder. // Restore ip to holder's context. - ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset)); + ldr(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset)); } // Check that the security token in the calling global object is @@ -1553,7 +1592,11 @@ void MacroAssembler::AllocateInNewSpace(int object_size, Register topaddr = scratch1; Register obj_size_reg = scratch2; mov(topaddr, Operand(new_space_allocation_top)); - mov(obj_size_reg, Operand(object_size)); + Operand obj_size_operand = Operand(object_size); + if (!obj_size_operand.is_single_instruction(this)) { + // We are about to steal IP, so we need to load this value first + mov(obj_size_reg, obj_size_operand); + } // This code stores a temporary value in ip. This is OK, as the code below // does not need ip for implicit literal generation. @@ -1575,7 +1618,13 @@ void MacroAssembler::AllocateInNewSpace(int object_size, // Calculate new top and bail out if new space is exhausted. Use result // to calculate the new top. - add(scratch2, result, Operand(obj_size_reg), SetCC); + if (obj_size_operand.is_single_instruction(this)) { + // We can add the size as an immediate + add(scratch2, result, obj_size_operand, SetCC); + } else { + // Doesn't fit in an immediate, we have to use the register + add(scratch2, result, obj_size_reg, SetCC); + } b(cs, gc_required); cmp(scratch2, Operand(ip)); b(hi, gc_required); @@ -1868,10 +1917,12 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue)); b(hi, fail); } @@ -1879,22 +1930,25 @@ void MacroAssembler::CheckFastElements(Register map, void MacroAssembler::CheckFastObjectElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); - STATIC_ASSERT(FAST_ELEMENTS == 1); + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); + STATIC_ASSERT(FAST_ELEMENTS == 2); + STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); b(ls, fail); - cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue)); b(hi, fail); } -void MacroAssembler::CheckFastSmiOnlyElements(Register map, - Register scratch, - Label* fail) { - STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0); +void MacroAssembler::CheckFastSmiElements(Register map, + Register scratch, + Label* fail) { + STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); + STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); - cmp(scratch, Operand(Map::kMaximumBitField2FastSmiOnlyElementValue)); + cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); b(hi, fail); } @@ -1962,13 +2016,13 @@ void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, // scratch1 is now effective address of the double element FloatingPointHelper::Destination destination; - if (CpuFeatures::IsSupported(VFP3)) { + if (CpuFeatures::IsSupported(VFP2)) { destination = FloatingPointHelper::kVFPRegisters; } else { destination = FloatingPointHelper::kCoreRegisters; } - Register untagged_value = receiver_reg; + Register untagged_value = elements_reg; SmiUntag(untagged_value, value_reg); FloatingPointHelper::ConvertIntToDouble(this, untagged_value, @@ -1979,7 +2033,7 @@ void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, scratch4, s2); if (destination == FloatingPointHelper::kVFPRegisters) { - CpuFeatures::Scope scope(VFP3); + CpuFeatures::Scope scope(VFP2); vstr(d0, scratch1, 0); } else { str(mantissa_reg, MemOperand(scratch1, 0)); @@ -1995,24 +2049,27 @@ void MacroAssembler::CompareMap(Register obj, Label* early_success, CompareMapMode mode) { ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); - cmp(scratch, Operand(map)); - if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { - Map* transitioned_fast_element_map( - map->LookupElementsTransitionMap(FAST_ELEMENTS, NULL)); - ASSERT(transitioned_fast_element_map == NULL || - map->elements_kind() != FAST_ELEMENTS); - if (transitioned_fast_element_map != NULL) { - b(eq, early_success); - cmp(scratch, Operand(Handle<Map>(transitioned_fast_element_map))); - } + CompareMap(scratch, map, early_success, mode); +} + - Map* transitioned_double_map( - map->LookupElementsTransitionMap(FAST_DOUBLE_ELEMENTS, NULL)); - ASSERT(transitioned_double_map == NULL || - map->elements_kind() == FAST_SMI_ONLY_ELEMENTS); - if (transitioned_double_map != NULL) { - b(eq, early_success); - cmp(scratch, Operand(Handle<Map>(transitioned_double_map))); +void MacroAssembler::CompareMap(Register obj_map, + Handle<Map> map, + Label* early_success, + CompareMapMode mode) { + cmp(obj_map, Operand(map)); + if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) { + ElementsKind kind = map->elements_kind(); + if (IsFastElementsKind(kind)) { + bool packed = IsFastPackedElementsKind(kind); + Map* current_map = *map; + while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) { + kind = GetNextMoreGeneralFastElementsKind(kind, packed); + current_map = current_map->LookupElementsTransitionMap(kind); + if (!current_map) break; + b(eq, early_success); + cmp(obj_map, Operand(Handle<Map>(current_map))); + } } } } @@ -2127,7 +2184,7 @@ void MacroAssembler::TryGetFunctionPrototype(Register function, void MacroAssembler::CallStub(CodeStub* stub, Condition cond) { ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. - Call(stub->GetCode(), RelocInfo::CODE_TARGET, kNoASTId, cond); + Call(stub->GetCode(), RelocInfo::CODE_TARGET, TypeFeedbackId::None(), cond); } @@ -2323,8 +2380,8 @@ void MacroAssembler::ConvertToInt32(Register source, Register scratch2, DwVfpRegister double_scratch, Label *not_int32) { - if (CpuFeatures::IsSupported(VFP3)) { - CpuFeatures::Scope scope(VFP3); + if (CpuFeatures::IsSupported(VFP2)) { + CpuFeatures::Scope scope(VFP2); sub(scratch, source, Operand(kHeapObjectTag)); vldr(double_scratch, scratch, HeapNumber::kValueOffset); vcvt_s32_f64(double_scratch.low(), double_scratch); @@ -2414,16 +2471,27 @@ void MacroAssembler::ConvertToInt32(Register source, void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode, - SwVfpRegister result, + Register result, DwVfpRegister double_input, - Register scratch1, - Register scratch2, + Register scratch, + DwVfpRegister double_scratch, CheckForInexactConversion check_inexact) { - ASSERT(CpuFeatures::IsSupported(VFP3)); - CpuFeatures::Scope scope(VFP3); - Register prev_fpscr = scratch1; - Register scratch = scratch2; + ASSERT(!result.is(scratch)); + ASSERT(!double_input.is(double_scratch)); + + ASSERT(CpuFeatures::IsSupported(VFP2)); + CpuFeatures::Scope scope(VFP2); + Register prev_fpscr = result; + Label done; + // Test for values that can be exactly represented as a signed 32-bit integer. + vcvt_s32_f64(double_scratch.low(), double_input); + vmov(result, double_scratch.low()); + vcvt_f64_s32(double_scratch, double_scratch.low()); + VFPCompareAndSetFlags(double_input, double_scratch); + b(eq, &done); + + // Convert to integer, respecting rounding mode. int32_t check_inexact_conversion = (check_inexact == kCheckForInexactConversion) ? kVFPInexactExceptionBit : 0; @@ -2445,7 +2513,7 @@ void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode, vmsr(scratch); // Convert the argument to an integer. - vcvt_s32_f64(result, + vcvt_s32_f64(double_scratch.low(), double_input, (rounding_mode == kRoundToZero) ? kDefaultRoundToZero : kFPSCRRounding); @@ -2454,8 +2522,12 @@ void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode, vmrs(scratch); // Restore FPSCR. vmsr(prev_fpscr); + // Move the converted value into the result register. + vmov(result, double_scratch.low()); // Check for vfp exceptions. tst(scratch, Operand(kVFPExceptionMask | check_inexact_conversion)); + + bind(&done); } @@ -2538,7 +2610,7 @@ void MacroAssembler::EmitECMATruncate(Register result, Register scratch, Register input_high, Register input_low) { - CpuFeatures::Scope scope(VFP3); + CpuFeatures::Scope scope(VFP2); ASSERT(!input_high.is(result)); ASSERT(!input_low.is(result)); ASSERT(!input_low.is(input_high)); @@ -2577,7 +2649,7 @@ void MacroAssembler::EmitECMATruncate(Register result, void MacroAssembler::GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits) { - if (CpuFeatures::IsSupported(ARMv7)) { + if (CpuFeatures::IsSupported(ARMv7) && !predictable_code_size()) { ubfx(dst, src, kSmiTagSize, num_least_bits); } else { mov(dst, Operand(src, ASR, kSmiTagSize)); @@ -2695,7 +2767,8 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, void MacroAssembler::GetBuiltinFunction(Register target, Builtins::JavaScript id) { // Load the builtins object into target register. - ldr(target, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + ldr(target, + MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); ldr(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset)); // Load the JavaScript builtin function from the builtins object. ldr(target, FieldMemOperand(target, @@ -2861,32 +2934,44 @@ void MacroAssembler::LoadTransitionedArrayMapConditional( Register scratch, Label* no_map_match) { // Load the global or builtins object from the current context. - ldr(scratch, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - ldr(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset)); + ldr(scratch, + MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + ldr(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset)); // Check that the function's map is the same as the expected cached map. - int expected_index = - Context::GetContextMapIndexFromElementsKind(expected_kind); - ldr(ip, MemOperand(scratch, Context::SlotOffset(expected_index))); + ldr(scratch, + MemOperand(scratch, + Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX))); + size_t offset = expected_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + ldr(ip, FieldMemOperand(scratch, offset)); cmp(map_in_out, ip); b(ne, no_map_match); // Use the transitioned cached map. - int trans_index = - Context::GetContextMapIndexFromElementsKind(transitioned_kind); - ldr(map_in_out, MemOperand(scratch, Context::SlotOffset(trans_index))); + offset = transitioned_kind * kPointerSize + + FixedArrayBase::kHeaderSize; + ldr(map_in_out, FieldMemOperand(scratch, offset)); } void MacroAssembler::LoadInitialArrayMap( - Register function_in, Register scratch, Register map_out) { + Register function_in, Register scratch, + Register map_out, bool can_have_holes) { ASSERT(!function_in.is(map_out)); Label done; ldr(map_out, FieldMemOperand(function_in, JSFunction::kPrototypeOrInitialMapOffset)); if (!FLAG_smi_only_arrays) { - LoadTransitionedArrayMapConditional(FAST_SMI_ONLY_ELEMENTS, - FAST_ELEMENTS, + ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS; + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + kind, + map_out, + scratch, + &done); + } else if (can_have_holes) { + LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS, + FAST_HOLEY_SMI_ELEMENTS, map_out, scratch, &done); @@ -2897,11 +2982,12 @@ void MacroAssembler::LoadInitialArrayMap( void MacroAssembler::LoadGlobalFunction(int index, Register function) { // Load the global or builtins object from the current context. - ldr(function, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - // Load the global context from the global or builtins object. + ldr(function, + MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX))); + // Load the native context from the global or builtins object. ldr(function, FieldMemOperand(function, - GlobalObject::kGlobalContextOffset)); - // Load the function from the global context. + GlobalObject::kNativeContextOffset)); + // Load the function from the native context. ldr(function, MemOperand(function, Context::SlotOffset(index))); } @@ -2981,38 +3067,46 @@ void MacroAssembler::JumpIfEitherSmi(Register reg1, } -void MacroAssembler::AbortIfSmi(Register object) { - STATIC_ASSERT(kSmiTag == 0); - tst(object, Operand(kSmiTagMask)); - Assert(ne, "Operand is a smi"); +void MacroAssembler::AssertNotSmi(Register object) { + if (emit_debug_code()) { + STATIC_ASSERT(kSmiTag == 0); + tst(object, Operand(kSmiTagMask)); + Check(ne, "Operand is a smi"); + } } -void MacroAssembler::AbortIfNotSmi(Register object) { - STATIC_ASSERT(kSmiTag == 0); - tst(object, Operand(kSmiTagMask)); - Assert(eq, "Operand is not smi"); +void MacroAssembler::AssertSmi(Register object) { + if (emit_debug_code()) { + STATIC_ASSERT(kSmiTag == 0); + tst(object, Operand(kSmiTagMask)); + Check(eq, "Operand is not smi"); + } } -void MacroAssembler::AbortIfNotString(Register object) { - STATIC_ASSERT(kSmiTag == 0); - tst(object, Operand(kSmiTagMask)); - Assert(ne, "Operand is not a string"); - push(object); - ldr(object, FieldMemOperand(object, HeapObject::kMapOffset)); - CompareInstanceType(object, object, FIRST_NONSTRING_TYPE); - pop(object); - Assert(lo, "Operand is not a string"); +void MacroAssembler::AssertString(Register object) { + if (emit_debug_code()) { + STATIC_ASSERT(kSmiTag == 0); + tst(object, Operand(kSmiTagMask)); + Check(ne, "Operand is a smi and not a string"); + push(object); + ldr(object, FieldMemOperand(object, HeapObject::kMapOffset)); + CompareInstanceType(object, object, FIRST_NONSTRING_TYPE); + pop(object); + Check(lo, "Operand is not a string"); + } } -void MacroAssembler::AbortIfNotRootValue(Register src, - Heap::RootListIndex root_value_index, - const char* message) { - CompareRoot(src, root_value_index); - Assert(eq, message); +void MacroAssembler::AssertRootValue(Register src, + Heap::RootListIndex root_value_index, + const char* message) { + if (emit_debug_code()) { + CompareRoot(src, root_value_index); + Check(eq, message); + } } @@ -3070,7 +3164,8 @@ void MacroAssembler::AllocateHeapNumber(Register result, Register scratch1, Register scratch2, Register heap_number_map, - Label* gc_required) { + Label* gc_required, + TaggingMode tagging_mode) { // Allocate an object in the heap for the heap number and tag it as a heap // object. AllocateInNewSpace(HeapNumber::kSize, @@ -3078,11 +3173,16 @@ void MacroAssembler::AllocateHeapNumber(Register result, scratch1, scratch2, gc_required, - TAG_OBJECT); + tagging_mode == TAG_RESULT ? TAG_OBJECT : + NO_ALLOCATION_FLAGS); // Store heap number map in the allocated object. AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); - str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset)); + if (tagging_mode == TAG_RESULT) { + str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset)); + } else { + str(heap_number_map, MemOperand(result, HeapObject::kMapOffset)); + } } @@ -3153,17 +3253,17 @@ void MacroAssembler::CopyBytes(Register src, cmp(length, Operand(kPointerSize)); b(lt, &byte_loop); ldr(scratch, MemOperand(src, kPointerSize, PostIndex)); -#if CAN_USE_UNALIGNED_ACCESSES - str(scratch, MemOperand(dst, kPointerSize, PostIndex)); -#else - strb(scratch, MemOperand(dst, 1, PostIndex)); - mov(scratch, Operand(scratch, LSR, 8)); - strb(scratch, MemOperand(dst, 1, PostIndex)); - mov(scratch, Operand(scratch, LSR, 8)); - strb(scratch, MemOperand(dst, 1, PostIndex)); - mov(scratch, Operand(scratch, LSR, 8)); - strb(scratch, MemOperand(dst, 1, PostIndex)); -#endif + if (CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) { + str(scratch, MemOperand(dst, kPointerSize, PostIndex)); + } else { + strb(scratch, MemOperand(dst, 1, PostIndex)); + mov(scratch, Operand(scratch, LSR, 8)); + strb(scratch, MemOperand(dst, 1, PostIndex)); + mov(scratch, Operand(scratch, LSR, 8)); + strb(scratch, MemOperand(dst, 1, PostIndex)); + mov(scratch, Operand(scratch, LSR, 8)); + strb(scratch, MemOperand(dst, 1, PostIndex)); + } sub(length, length, Operand(kPointerSize)); b(&word_loop); @@ -3313,6 +3413,7 @@ void MacroAssembler::PrepareCallCFunction(int num_reg_arguments, void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) { + ASSERT(CpuFeatures::IsSupported(VFP2)); if (use_eabi_hardfloat()) { Move(d0, dreg); } else { @@ -3323,6 +3424,7 @@ void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) { void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2) { + ASSERT(CpuFeatures::IsSupported(VFP2)); if (use_eabi_hardfloat()) { if (dreg2.is(d0)) { ASSERT(!dreg1.is(d1)); @@ -3341,6 +3443,7 @@ void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1, void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg, Register reg) { + ASSERT(CpuFeatures::IsSupported(VFP2)); if (use_eabi_hardfloat()) { Move(d0, dreg); Move(r0, reg); @@ -3442,7 +3545,7 @@ void MacroAssembler::CheckPageFlag( int mask, Condition cc, Label* condition_met) { - and_(scratch, object, Operand(~Page::kPageAlignmentMask)); + Bfc(scratch, object, 0, kPageSizeBits); ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); tst(scratch, Operand(mask)); b(cc, condition_met); @@ -3591,7 +3694,7 @@ void MacroAssembler::EnsureNotWhite( // 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(kOneByteStringTag == 4 && kStringEncodingMask == 4); ASSERT(kSmiTag == 0 && kSmiTagSize == 1); ldr(ip, FieldMemOperand(value, String::kLengthOffset)); tst(instance_type, Operand(kStringEncodingMask)); @@ -3637,7 +3740,7 @@ void MacroAssembler::ClampDoubleToUint8(Register result_reg, // Double value is >= 255, return 255. bind(&above_zero); - Vmov(temp_double_reg, 255.0); + Vmov(temp_double_reg, 255.0, result_reg); VFPCompareAndSetFlags(input_reg, temp_double_reg); b(le, &in_bounds); mov(result_reg, Operand(255)); @@ -3645,67 +3748,72 @@ void MacroAssembler::ClampDoubleToUint8(Register result_reg, // In 0-255 range, round and truncate. bind(&in_bounds); - Vmov(temp_double_reg, 0.5); - vadd(temp_double_reg, input_reg, temp_double_reg); - vcvt_u32_f64(temp_double_reg.low(), temp_double_reg); - vmov(result_reg, temp_double_reg.low()); + // Save FPSCR. + vmrs(ip); + // Set rounding mode to round to the nearest integer by clearing bits[23:22]. + bic(result_reg, ip, Operand(kVFPRoundingModeMask)); + vmsr(result_reg); + vcvt_s32_f64(input_reg.low(), input_reg, kFPSCRRounding); + vmov(result_reg, input_reg.low()); + // Restore FPSCR. + vmsr(ip); bind(&done); } void MacroAssembler::LoadInstanceDescriptors(Register map, Register descriptors) { - ldr(descriptors, - FieldMemOperand(map, Map::kInstanceDescriptorsOrBitField3Offset)); - Label not_smi; - JumpIfNotSmi(descriptors, ¬_smi); - mov(descriptors, Operand(FACTORY->empty_descriptor_array())); - bind(¬_smi); + ldr(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset)); +} + + +void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { + ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); + DecodeField<Map::NumberOfOwnDescriptorsBits>(dst); +} + + +void MacroAssembler::EnumLength(Register dst, Register map) { + STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); + ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); + and_(dst, dst, Operand(Smi::FromInt(Map::EnumLengthBits::kMask))); } void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { - Label next; - // Preload a couple of values used in the loop. Register empty_fixed_array_value = r6; LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Register empty_descriptor_array_value = r7; - LoadRoot(empty_descriptor_array_value, - Heap::kEmptyDescriptorArrayRootIndex); - mov(r1, r0); - bind(&next); + Label next, start; + mov(r2, r0); - // Check that there are no elements. Register r1 contains the - // current JS object we've reached through the prototype chain. - ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); - cmp(r2, empty_fixed_array_value); - b(ne, call_runtime); + // Check if the enum length field is properly initialized, indicating that + // there is an enum cache. + ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in r2 for the subsequent - // prototype load. - ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - ldr(r3, FieldMemOperand(r2, Map::kInstanceDescriptorsOrBitField3Offset)); - JumpIfSmi(r3, call_runtime); + EnumLength(r3, r1); + cmp(r3, Operand(Smi::FromInt(Map::kInvalidEnumCache))); + b(eq, call_runtime); - // Check that there is an enum cache in the non-empty instance - // descriptors (r3). This is the case if the next enumeration - // index field does not contain a smi. - ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumerationIndexOffset)); - JumpIfSmi(r3, call_runtime); + jmp(&start); + + bind(&next); + ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - cmp(r1, r0); - b(eq, &check_prototype); - ldr(r3, FieldMemOperand(r3, DescriptorArray::kEnumCacheBridgeCacheOffset)); - cmp(r3, empty_fixed_array_value); + EnumLength(r3, r1); + cmp(r3, Operand(Smi::FromInt(0))); + b(ne, call_runtime); + + bind(&start); + + // Check that there are no elements. Register r2 contains the current JS + // object we've reached through the prototype chain. + ldr(r2, FieldMemOperand(r2, JSObject::kElementsOffset)); + cmp(r2, empty_fixed_array_value); b(ne, call_runtime); - // Load the prototype from the map and loop if non-null. - bind(&check_prototype); - ldr(r1, FieldMemOperand(r2, Map::kPrototypeOffset)); - cmp(r1, null_value); + ldr(r2, FieldMemOperand(r1, Map::kPrototypeOffset)); + cmp(r2, null_value); b(ne, &next); } |