From 43e248d2e52ed1e3bb10ce4ec355168bc9cf4059 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Thu, 18 Oct 2012 12:21:42 +0000 Subject: [PATCH] Use movw/movt instead of constant pool on ARMv7. Some ARM architectures load 32-bit immediate constants more efficiently using movw/movt pairs rather than constant pool loads. This patch allows the assembler to generate one or the other load form at runtime depending on what is faster. R=ulan@chromium.org Review URL: https://codereview.chromium.org/11037023 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12755 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/assembler-arm-inl.h | 136 +++++++++++++++++++++++++++++++++----- src/arm/assembler-arm.cc | 145 ++++++++++++++++++++++++++++------------- src/arm/assembler-arm.h | 82 +++++++++++++++++------ src/arm/code-stubs-arm.cc | 3 +- src/arm/debug-arm.cc | 4 +- src/arm/full-codegen-arm.cc | 7 +- src/arm/ic-arm.cc | 2 +- src/arm/lithium-codegen-arm.cc | 31 +++++---- src/arm/lithium-codegen-arm.h | 20 +++--- src/arm/macro-assembler-arm.cc | 46 ++++++++----- src/arm/macro-assembler-arm.h | 12 +++- src/assembler.h | 8 +-- src/debug.cc | 4 +- src/flag-definitions.h | 3 + src/ia32/assembler-ia32-inl.h | 5 ++ src/ia32/assembler-ia32.h | 5 ++ src/ic-inl.h | 2 +- src/ic.cc | 2 +- src/mark-compact.cc | 5 +- src/platform-linux.cc | 18 +++++ src/platform-nullos.cc | 7 +- src/platform.h | 3 + src/v8globals.h | 8 +++ src/x64/assembler-x64-inl.h | 6 ++ src/x64/assembler-x64.h | 5 ++ 25 files changed, 431 insertions(+), 138 deletions(-) diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h index c47c094..6268c33 100644 --- a/src/arm/assembler-arm-inl.h +++ b/src/arm/assembler-arm-inl.h @@ -75,7 +75,7 @@ Address RelocInfo::target_address_address() { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY || rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE); - return reinterpret_cast
(Assembler::target_address_address_at(pc_)); + return reinterpret_cast
(Assembler::target_pointer_address_at(pc_)); } @@ -86,7 +86,8 @@ int RelocInfo::target_address_size() { void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); - Assembler::set_target_address_at(pc_, target); + Assembler::set_target_address_at(pc_, reinterpret_cast
( + reinterpret_cast(target) & ~3)); if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { Object* target_code = Code::GetCodeFromTargetAddress(target); host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( @@ -97,25 +98,30 @@ void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { Object* RelocInfo::target_object() { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return Memory::Object_at(Assembler::target_address_address_at(pc_)); + return reinterpret_cast(Assembler::target_pointer_at(pc_)); } Handle RelocInfo::target_object_handle(Assembler* origin) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return Memory::Object_Handle_at(Assembler::target_address_address_at(pc_)); + return Handle(reinterpret_cast( + Assembler::target_pointer_at(pc_))); } Object** RelocInfo::target_object_address() { + // Provide a "natural pointer" to the embedded object, + // which can be de-referenced during heap iteration. ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - return reinterpret_cast(Assembler::target_address_address_at(pc_)); + reconstructed_obj_ptr_ = + reinterpret_cast(Assembler::target_pointer_at(pc_)); + return &reconstructed_obj_ptr_; } void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); - Assembler::set_target_address_at(pc_, reinterpret_cast
(target)); + Assembler::set_target_pointer_at(pc_, reinterpret_cast
(target)); if (mode == UPDATE_WRITE_BARRIER && host() != NULL && target->IsHeapObject()) { @@ -127,7 +133,8 @@ void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { Address* RelocInfo::target_reference_address() { ASSERT(rmode_ == EXTERNAL_REFERENCE); - return reinterpret_cast(Assembler::target_address_address_at(pc_)); + reconstructed_adr_ptr_ = Assembler::target_address_at(pc_); + return &reconstructed_adr_ptr_; } @@ -326,7 +333,7 @@ void Assembler::emit(Instr x) { } -Address Assembler::target_address_address_at(Address pc) { +Address Assembler::target_pointer_address_at(Address pc) { Address target_pc = pc; Instr instr = Memory::int32_at(target_pc); // If we have a bx instruction, the instruction before the bx is @@ -356,8 +363,63 @@ Address Assembler::target_address_address_at(Address pc) { } -Address Assembler::target_address_at(Address pc) { - return Memory::Address_at(target_address_address_at(pc)); +Address Assembler::target_pointer_at(Address pc) { + if (IsMovW(Memory::int32_at(pc))) { + ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); + Instruction* instr = Instruction::At(pc); + Instruction* next_instr = Instruction::At(pc + kInstrSize); + return reinterpret_cast
( + (next_instr->ImmedMovwMovtValue() << 16) | + instr->ImmedMovwMovtValue()); + } + return Memory::Address_at(target_pointer_address_at(pc)); +} + + +Address Assembler::target_address_from_return_address(Address pc) { + // Returns the address of the call target from the return address that will + // be returned to after a call. +#ifdef USE_BLX + // Call sequence on V7 or later is : + // movw ip, #... @ call address low 16 + // movt ip, #... @ call address high 16 + // blx ip + // @ return address + // Or pre-V7 or cases that need frequent patching: + // ldr ip, [pc, #...] @ call address + // blx ip + // @ return address + Address candidate = pc - 2 * Assembler::kInstrSize; + Instr candidate_instr(Memory::int32_at(candidate)); + if (IsLdrPcImmediateOffset(candidate_instr)) { + return candidate; + } + candidate = pc - 3 * Assembler::kInstrSize; + ASSERT(IsMovW(Memory::int32_at(candidate)) && + IsMovT(Memory::int32_at(candidate + kInstrSize))); + return candidate; +#else + // Call sequence is: + // mov lr, pc + // ldr pc, [pc, #...] @ call address + // @ return address + return pc - kInstrSize; +#endif +} + + +Address Assembler::return_address_from_call_start(Address pc) { +#ifdef USE_BLX + if (IsLdrPcImmediateOffset(Memory::int32_at(pc))) { + return pc + kInstrSize * 2; + } else { + ASSERT(IsMovW(Memory::int32_at(pc))); + ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); + return pc + kInstrSize * 3; + } +#else + return pc + kInstrSize; +#endif } @@ -373,17 +435,55 @@ void Assembler::set_external_target_at(Address constant_pool_entry, } +static Instr EncodeMovwImmediate(uint32_t immediate) { + ASSERT(immediate < 0x10000); + return ((immediate & 0xf000) << 4) | (immediate & 0xfff); +} + + +void Assembler::set_target_pointer_at(Address pc, Address target) { + if (IsMovW(Memory::int32_at(pc))) { + ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); + uint32_t* instr_ptr = reinterpret_cast(pc); + uint32_t immediate = reinterpret_cast(target); + uint32_t intermediate = instr_ptr[0]; + intermediate &= ~EncodeMovwImmediate(0xFFFF); + intermediate |= EncodeMovwImmediate(immediate & 0xFFFF); + instr_ptr[0] = intermediate; + intermediate = instr_ptr[1]; + intermediate &= ~EncodeMovwImmediate(0xFFFF); + intermediate |= EncodeMovwImmediate(immediate >> 16); + instr_ptr[1] = intermediate; + ASSERT(IsMovW(Memory::int32_at(pc))); + ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); + CPU::FlushICache(pc, 2 * kInstrSize); + } else { + ASSERT(IsLdrPcImmediateOffset(Memory::int32_at(pc))); + Memory::Address_at(target_pointer_address_at(pc)) = target; + // Intuitively, we would think it is necessary to always flush the + // instruction cache after patching a target address in the code as follows: + // CPU::FlushICache(pc, sizeof(target)); + // However, on ARM, no instruction is actually patched in the case + // of embedded constants of the form: + // ldr ip, [pc, #...] + // since the instruction accessing this address in the constant pool remains + // unchanged. + } +} + + +Address Assembler::target_address_at(Address pc) { + return reinterpret_cast
( + reinterpret_cast(target_pointer_at(pc)) & ~3); +} + + void Assembler::set_target_address_at(Address pc, Address target) { - Memory::Address_at(target_address_address_at(pc)) = target; - // Intuitively, we would think it is necessary to flush the instruction cache - // after patching a target address in the code as follows: - // CPU::FlushICache(pc, sizeof(target)); - // However, on ARM, no instruction was actually patched by the assignment - // above; the target address is not part of an instruction, it is patched in - // the constant pool and is read via a data access; the instruction accessing - // this address in the constant pool remains unchanged. + set_target_pointer_at(pc, reinterpret_cast
( + reinterpret_cast(target) & ~3)); } + } } // namespace v8::internal #endif // V8_ARM_ASSEMBLER_ARM_INL_H_ diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index ccf7208..cc3d5b1 100644 --- a/src/arm/assembler-arm.cc +++ b/src/arm/assembler-arm.cc @@ -117,6 +117,10 @@ void CpuFeatures::Probe() { if (FLAG_enable_sudiv) { supported_ |= 1u << SUDIV; } + + if (FLAG_enable_movw_movt) { + supported_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; + } #else // __arm__ // Probe for additional features not already known to be available. if (!IsSupported(VFP3) && OS::ArmCpuHasFeature(VFP3)) { @@ -140,6 +144,11 @@ void CpuFeatures::Probe() { found_by_runtime_probing_ |= 1u << UNALIGNED_ACCESSES; } + if (OS::GetCpuImplementer() == QUALCOMM_IMPLEMENTER && + OS::ArmCpuHasFeature(ARMv7)) { + found_by_runtime_probing_ |= 1u << MOVW_MOVT_IMMEDIATE_LOADS; + } + supported_ |= found_by_runtime_probing_; #endif @@ -730,12 +739,6 @@ void Assembler::next(Label* L) { } -static Instr EncodeMovwImmediate(uint32_t immediate) { - ASSERT(immediate < 0x10000); - return ((immediate & 0xf000) << 4) | (immediate & 0xfff); -} - - // Low-level code emission routines depending on the addressing mode. // If this returns true then you have to use the rotate_imm and immed_8 // that it returns, because it may have already changed the instruction @@ -800,7 +803,7 @@ static bool fits_shifter(uint32_t imm32, // if they can be encoded in the ARM's 12 bits of immediate-offset instruction // space. There is no guarantee that the relocated location can be similarly // encoded. -bool Operand::must_use_constant_pool(const Assembler* assembler) const { +bool Operand::must_output_reloc_info(const Assembler* assembler) const { if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) { #ifdef DEBUG if (!Serializer::enabled()) { @@ -820,21 +823,29 @@ bool Operand::is_single_instruction(const Assembler* assembler, Instr instr) const { if (rm_.is_valid()) return true; uint32_t dummy1, dummy2; - if (must_use_constant_pool(assembler) || + if (must_output_reloc_info(assembler) || !fits_shifter(imm32_, &dummy1, &dummy2, &instr)) { // The immediate operand cannot be encoded as a shifter operand, or use of // constant pool is required. For a mov instruction not setting the // condition code additional instruction conventions can be used. if ((instr & ~kCondMask) == 13*B21) { // mov, S not set - if (must_use_constant_pool(assembler) || - !CpuFeatures::IsSupported(ARMv7)) { - // mov instruction will be an ldr from constant pool (one instruction). - return true; - } else { - // mov instruction will be a mov or movw followed by movt (two - // instructions). - return false; - } +#ifdef USE_BLX + // When using BLX, there are two things that must be true for the address + // load to be longer than a single instruction. First, immediate loads + // using movw/movt must be supported (and fast) on the target ARM + // architecture. Second, the reloc mode must be something other than NONE, + // since NONE is a used whenever the constant pool cannot be used for + // technical reasons, e.g. back-patching calls site in optimized code with + // a call to a lazy deopt routine. + return !Assembler::allow_immediate_constant_pool_loads(assembler) && + rmode_ != RelocInfo::NONE; +#else + // It's not possible to use immediate loads to the pc to do a call, (the + // pc would be inconsistent half-way through the load), so loading the + // destination address without USE_BLX is always a single instruction of + // the form ldr pc, [pc + #xxx]. + return true; +#endif } else { // If this is not a mov or mvn instruction there will always an additional // instructions - either mov or ldr. The mov might actually be two @@ -850,6 +861,34 @@ bool Operand::is_single_instruction(const Assembler* assembler, } +void Assembler::move_32_bit_immediate(Condition cond, + Register rd, + SBit s, + const Operand& x) { + if (rd.code() != pc.code() && s == LeaveCC) { + // Candidate for immediate load. + if (x.must_output_reloc_info(this)) { + if (!Assembler::allow_immediate_constant_pool_loads(this)) { + RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL); + ldr(rd, MemOperand(pc, 0), cond); + return; + } + RecordRelocInfo(x.rmode_, x.imm32_, DONT_USE_CONSTANT_POOL); + // Make sure the movw/movt doesn't get separated. + BlockConstPoolFor(2); + } + + // Emit a real movw/movt pair. + emit(cond | 0x30*B20 | rd.code()*B12 | + EncodeMovwImmediate(x.imm32_ & 0xffff)); + movt(rd, static_cast(x.imm32_) >> 16, cond); + } else { + RecordRelocInfo(x.rmode_, x.imm32_, USE_CONSTANT_POOL); + ldr(rd, MemOperand(pc, 0), cond); + } +} + + void Assembler::addrmod1(Instr instr, Register rn, Register rd, @@ -860,7 +899,7 @@ void Assembler::addrmod1(Instr instr, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (x.must_use_constant_pool(this) || + if (x.must_output_reloc_info(this) || !fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) { // The immediate operand cannot be encoded as a shifter operand, so load // it first to register ip and change the original instruction to use ip. @@ -869,24 +908,16 @@ void Assembler::addrmod1(Instr instr, CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed Condition cond = Instruction::ConditionField(instr); if ((instr & ~kCondMask) == 13*B21) { // mov, S not set - if (x.must_use_constant_pool(this) || - !CpuFeatures::IsSupported(ARMv7)) { - RecordRelocInfo(x.rmode_, x.imm32_); - ldr(rd, MemOperand(pc, 0), cond); - } else { - // Will probably use movw, will certainly not use constant pool. - mov(rd, Operand(x.imm32_ & 0xffff), LeaveCC, cond); - movt(rd, static_cast(x.imm32_) >> 16, cond); - } + move_32_bit_immediate(cond, rd, LeaveCC, x); } else { // If this is not a mov or mvn instruction we may still be able to avoid // a constant pool entry by using mvn or movw. - if (!x.must_use_constant_pool(this) && + if (!x.must_output_reloc_info(this) && (instr & kMovMvnMask) != kMovMvnPattern) { mov(ip, x, LeaveCC, cond); } else { - RecordRelocInfo(x.rmode_, x.imm32_); - ldr(ip, MemOperand(pc, 0), cond); + move_32_bit_immediate(cond, ip, + static_cast(instr & (1 << 20)), x); } addrmod1(instr, rn, rd, Operand(ip)); } @@ -1193,6 +1224,9 @@ void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) { void Assembler::movw(Register reg, uint32_t immediate, Condition cond) { ASSERT(immediate < 0x10000); + // May use movw if supported, but on unsupported platforms will try to use + // equivalent rotated immed_8 value and other tricks before falling back to a + // constant pool load. mov(reg, Operand(immediate), LeaveCC, cond); } @@ -1422,7 +1456,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (src.must_use_constant_pool(this) || + if (src.must_output_reloc_info(this) || !fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) { // Immediate operand cannot be encoded, load it first to register ip. RecordRelocInfo(src.rmode_, src.imm32_); @@ -2450,6 +2484,22 @@ void Assembler::nop(int type) { } +bool Assembler::IsMovT(Instr instr) { + instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions + ((kNumRegisters-1)*B12) | // mask out register + EncodeMovwImmediate(0xFFFF)); // mask out immediate value + return instr == 0x34*B20; +} + + +bool Assembler::IsMovW(Instr instr) { + instr &= ~(((kNumberOfConditions - 1) << 28) | // Mask off conditions + ((kNumRegisters-1)*B12) | // mask out destination + EncodeMovwImmediate(0xFFFF)); // mask out immediate value + return instr == 0x30*B20; +} + + bool Assembler::IsNop(Instr instr, int type) { ASSERT(0 <= type && type <= 14); // mov pc, pc isn't a nop. // Check for mov rx, rx where x = type. @@ -2568,18 +2618,21 @@ void Assembler::dd(uint32_t data) { } -void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { +void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data, + UseConstantPoolMode mode) { // We do not try to reuse pool constants. RelocInfo rinfo(pc_, rmode, data, NULL); if (((rmode >= RelocInfo::JS_RETURN) && (rmode <= RelocInfo::DEBUG_BREAK_SLOT)) || - (rmode == RelocInfo::CONST_POOL)) { + (rmode == RelocInfo::CONST_POOL) || + mode == DONT_USE_CONSTANT_POOL) { // Adjust code for new modes. ASSERT(RelocInfo::IsDebugBreakSlot(rmode) || RelocInfo::IsJSReturn(rmode) || RelocInfo::IsComment(rmode) || RelocInfo::IsPosition(rmode) - || RelocInfo::IsConstPool(rmode)); + || RelocInfo::IsConstPool(rmode) + || mode == DONT_USE_CONSTANT_POOL); // These modes do not need an entry in the constant pool. } else { ASSERT(num_pending_reloc_info_ < kMaxNumPendingRelocInfo); @@ -2698,17 +2751,19 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { Instr instr = instr_at(rinfo.pc()); // Instruction to patch must be 'ldr rd, [pc, #offset]' with offset == 0. - ASSERT(IsLdrPcImmediateOffset(instr) && - GetLdrRegisterImmediateOffset(instr) == 0); - - int delta = pc_ - rinfo.pc() - kPcLoadDelta; - // 0 is the smallest delta: - // ldr rd, [pc, #0] - // constant pool marker - // data - ASSERT(is_uint12(delta)); - - instr_at_put(rinfo.pc(), SetLdrRegisterImmediateOffset(instr, delta)); + if (IsLdrPcImmediateOffset(instr) && + GetLdrRegisterImmediateOffset(instr) == 0) { + int delta = pc_ - rinfo.pc() - kPcLoadDelta; + // 0 is the smallest delta: + // ldr rd, [pc, #0] + // constant pool marker + // data + ASSERT(is_uint12(delta)); + + instr_at_put(rinfo.pc(), SetLdrRegisterImmediateOffset(instr, delta)); + } else { + ASSERT(IsMovW(instr)); + } emit(rinfo.data()); } diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index 7c80b09..bee35af 100644 --- a/src/arm/assembler-arm.h +++ b/src/arm/assembler-arm.h @@ -425,7 +425,7 @@ class Operand BASE_EMBEDDED { // actual instruction to use is required for this calculation. For other // instructions instr is ignored. bool is_single_instruction(const Assembler* assembler, Instr instr = 0) const; - bool must_use_constant_pool(const Assembler* assembler) const; + bool must_output_reloc_info(const Assembler* assembler) const; inline int32_t immediate() const { ASSERT(!rm_.is_valid()); @@ -689,13 +689,25 @@ class Assembler : public AssemblerBase { void label_at_put(Label* L, int at_offset); // Return the address in the constant pool of the code target address used by - // the branch/call instruction at pc. - INLINE(static Address target_address_address_at(Address pc)); + // the branch/call instruction at pc, or the object in a mov. + INLINE(static Address target_pointer_address_at(Address pc)); + + // Read/Modify the pointer in the branch/call/move instruction at pc. + INLINE(static Address target_pointer_at(Address pc)); + INLINE(static void set_target_pointer_at(Address pc, Address target)); // Read/Modify the code target address in the branch/call instruction at pc. INLINE(static Address target_address_at(Address pc)); INLINE(static void set_target_address_at(Address pc, Address target)); + // Return the code target address at a call site from the return address + // of that call in the instruction stream. + INLINE(static Address target_address_from_return_address(Address pc)); + + // Given the address of the beginning of a call, return the address + // in the instruction stream that the call will return from. + INLINE(static Address return_address_from_call_start(Address pc)); + // This sets the branch destination (which is in the constant pool on ARM). // This is for calls and branches within generated code. inline static void deserialization_set_special_target_at( @@ -714,22 +726,6 @@ class Assembler : public AssemblerBase { // Size of an instruction. static const int kInstrSize = sizeof(Instr); - // Distance between the instruction referring to the address of the call - // target and the return address. -#ifdef USE_BLX - // Call sequence is: - // ldr ip, [pc, #...] @ call address - // blx ip - // @ return address - static const int kCallTargetAddressOffset = 2 * kInstrSize; -#else - // Call sequence is: - // mov lr, pc - // ldr pc, [pc, #...] @ call address - // @ return address - static const int kCallTargetAddressOffset = kInstrSize; -#endif - // Distance between start of patched return sequence and the emitted address // to jump to. #ifdef USE_BLX @@ -758,6 +754,12 @@ class Assembler : public AssemblerBase { static const int kPatchDebugBreakSlotAddressOffset = kInstrSize; #endif +#ifdef USE_BLX + static const int kPatchDebugBreakSlotReturnOffset = 2 * kInstrSize; +#else + static const int kPatchDebugBreakSlotReturnOffset = kInstrSize; +#endif + // Difference between address of current opcode and value read from pc // register. static const int kPcLoadDelta = 8; @@ -1185,6 +1187,12 @@ class Assembler : public AssemblerBase { bool predictable_code_size() const { return predictable_code_size_; } + static bool allow_immediate_constant_pool_loads( + const Assembler* assembler) { + return CpuFeatures::IsSupported(MOVW_MOVT_IMMEDIATE_LOADS) && + (assembler == NULL || !assembler->predictable_code_size()); + } + // Check the code size generated from label to here. int SizeOfCodeGeneratedSince(Label* label) { return pc_offset() - label->pos(); @@ -1305,6 +1313,8 @@ class Assembler : public AssemblerBase { static Register GetCmpImmediateRegister(Instr instr); static int GetCmpImmediateRawImmediate(Instr instr); static bool IsNop(Instr instr, int type = NON_MARKING_NOP); + static bool IsMovT(Instr instr); + static bool IsMovW(Instr instr); // Constants in pools are accessed via pc relative addressing, which can // reach +/-4KB thereby defining a maximum distance between the instruction @@ -1443,6 +1453,12 @@ class Assembler : public AssemblerBase { void GrowBuffer(); inline void emit(Instr x); + // 32-bit immediate values + void move_32_bit_immediate(Condition cond, + Register rd, + SBit s, + const Operand& x); + // Instruction generation void addrmod1(Instr instr, Register rn, Register rd, const Operand& x); void addrmod2(Instr instr, Register rd, const MemOperand& x); @@ -1456,8 +1472,14 @@ class Assembler : public AssemblerBase { void link_to(Label* L, Label* appendix); void next(Label* L); + enum UseConstantPoolMode { + USE_CONSTANT_POOL, + DONT_USE_CONSTANT_POOL + }; + // Record reloc info for current pc_ - void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0); + void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0, + UseConstantPoolMode mode = USE_CONSTANT_POOL); friend class RegExpMacroAssemblerARM; friend class RelocInfo; @@ -1482,6 +1504,26 @@ class EnsureSpace BASE_EMBEDDED { }; +class PredictableCodeSizeScope { + public: + explicit PredictableCodeSizeScope(Assembler* assembler) + : asm_(assembler) { + old_value_ = assembler->predictable_code_size(); + assembler->set_predictable_code_size(true); + } + + ~PredictableCodeSizeScope() { + if (!old_value_) { + asm_->set_predictable_code_size(false); + } + } + + private: + Assembler* asm_; + bool old_value_; +}; + + } } // namespace v8::internal #endif // V8_ARM_ASSEMBLER_ARM_H_ diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index a6aff15..ceb108f 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -7569,6 +7569,7 @@ void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) { void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { if (entry_hook_ != NULL) { + PredictableCodeSizeScope predictable(masm); ProfileEntryHookStub stub; __ push(lr); __ CallStub(&stub); @@ -7580,7 +7581,7 @@ void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { void ProfileEntryHookStub::Generate(MacroAssembler* masm) { // The entry hook is a "push lr" instruction, followed by a call. const int32_t kReturnAddressDistanceFromFunctionStart = - Assembler::kCallTargetAddressOffset + Assembler::kInstrSize; + 3 * Assembler::kInstrSize; // Save live volatile registers. __ Push(lr, r5, r1); diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc index 3e7a1e9..c2941be 100644 --- a/src/arm/debug-arm.cc +++ b/src/arm/debug-arm.cc @@ -48,7 +48,7 @@ void BreakLocationIterator::SetDebugBreakAtReturn() { // add sp, sp, #4 // bx lr // to a call to the debug break return code. - // #if USE_BLX + // #ifdef USE_BLX // ldr ip, [pc, #0] // blx ip // #else @@ -99,7 +99,7 @@ void BreakLocationIterator::SetDebugBreakAtSlot() { // mov r2, r2 // mov r2, r2 // to a call to the debug break slot code. - // #if USE_BLX + // #ifdef USE_BLX // ldr ip, [pc, #0] // blx ip // #else diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 1209372..5730d56 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -287,6 +287,7 @@ void FullCodeGenerator::Generate() { __ LoadRoot(ip, Heap::kStackLimitRootIndex); __ cmp(sp, Operand(ip)); __ b(hs, &ok); + PredictableCodeSizeScope predictable(masm_); StackCheckStub stub; __ CallStub(&stub); __ bind(&ok); @@ -364,6 +365,7 @@ void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt, __ LoadRoot(ip, Heap::kStackLimitRootIndex); __ cmp(sp, Operand(ip)); __ b(hs, &ok); + PredictableCodeSizeScope predictable(masm_); StackCheckStub stub; __ CallStub(&stub); } @@ -437,6 +439,7 @@ void FullCodeGenerator::EmitReturnSequence() { // tool from instrumenting as we rely on the code size here. int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize; CodeGenerator::RecordPositions(masm_, function()->end_position() - 1); + PredictableCodeSizeScope predictable(masm_); __ RecordJSReturn(); masm_->mov(sp, fp); masm_->ldm(ia_w, sp, fp.bit() | lr.bit()); @@ -2239,7 +2242,9 @@ void FullCodeGenerator::CallIC(Handle code, RelocInfo::Mode rmode, TypeFeedbackId ast_id) { ic_total_count_++; - __ Call(code, rmode, ast_id); + // All calls must have a predictable size in full-codegen code to ensure that + // the debugger can patch them correctly. + __ Call(code, rmode, ast_id, al, NEVER_INLINE_TARGET_ADDRESS); } void FullCodeGenerator::EmitCallWithIC(Call* expr, diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 87d09c0..4839589 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -1734,7 +1734,7 @@ void CompareIC::UpdateCaches(Handle x, Handle y) { void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) { Address cmp_instruction_address = - address + Assembler::kCallTargetAddressOffset; + Assembler::return_address_from_call_start(address); // If the instruction following the call is not a cmp rx, #yyy, nothing // was inlined. diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index d5b67a7..cabaa71 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -608,22 +608,24 @@ void LCodeGen::AddToTranslation(Translation* translation, void LCodeGen::CallCode(Handle code, RelocInfo::Mode mode, - LInstruction* instr) { - CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT); + LInstruction* instr, + TargetAddressStorageMode storage_mode) { + CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT, storage_mode); } void LCodeGen::CallCodeGeneric(Handle code, RelocInfo::Mode mode, LInstruction* instr, - SafepointMode safepoint_mode) { + SafepointMode safepoint_mode, + TargetAddressStorageMode storage_mode) { ASSERT(instr != NULL); // Block literal pool emission to ensure nop indicating no inlined smi code // is in the correct position. Assembler::BlockConstPoolScope block_const_pool(masm()); LPointerMap* pointers = instr->pointer_map(); RecordPosition(pointers->position()); - __ Call(code, mode); + __ Call(code, mode, TypeFeedbackId::None(), al, storage_mode); RecordSafepointWithLazyDeopt(instr, safepoint_mode); // Signal that we don't inline smi code before these stubs in the @@ -2471,6 +2473,7 @@ void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { // We use Factory::the_hole_value() on purpose instead of loading from the // root array to force relocation to be able to later patch with // the cached map. + PredictableCodeSizeScope predictable(masm_); Handle cell = factory()->NewJSGlobalPropertyCell(factory()->the_hole_value()); __ mov(ip, Operand(Handle(cell))); @@ -2532,6 +2535,9 @@ void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, ASSERT(temp.is(r4)); __ LoadHeapObject(InstanceofStub::right(), instr->function()); static const int kAdditionalDelta = 5; + // Make sure that code size is predicable, since we use specific constants + // offsets in the code to find embedded values.. + PredictableCodeSizeScope predictable(masm_); int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta; Label before_push_delta; __ bind(&before_push_delta); @@ -2795,7 +2801,7 @@ void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) { if (need_generic) { __ mov(r2, Operand(name)); Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); } __ bind(&done); } @@ -2808,7 +2814,7 @@ void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) { // Name is always in r2. __ mov(r2, Operand(instr->name())); Handle ic = isolate()->builtins()->LoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); } @@ -3119,7 +3125,7 @@ void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(r0)); Handle ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); } @@ -3814,7 +3820,7 @@ void LCodeGen::DoCallKeyed(LCallKeyed* instr) { int arity = instr->arity(); Handle ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(arity); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); } @@ -3827,7 +3833,7 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { Handle ic = isolate()->stub_cache()->ComputeCallInitialize(arity, mode); __ mov(r2, Operand(instr->name())); - CallCode(ic, mode, instr); + CallCode(ic, mode, instr, NEVER_INLINE_TARGET_ADDRESS); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); } @@ -3852,7 +3858,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { Handle ic = isolate()->stub_cache()->ComputeCallInitialize(arity, mode); __ mov(r2, Operand(instr->name())); - CallCode(ic, mode, instr); + CallCode(ic, mode, instr, NEVER_INLINE_TARGET_ADDRESS); __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); } @@ -3952,7 +3958,7 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { Handle ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); } @@ -4166,7 +4172,7 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { Handle ic = (instr->strict_mode_flag() == kStrictMode) ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() : isolate()->builtins()->KeyedStoreIC_Initialize(); - CallCode(ic, RelocInfo::CODE_TARGET, instr); + CallCode(ic, RelocInfo::CODE_TARGET, instr, NEVER_INLINE_TARGET_ADDRESS); } @@ -5541,6 +5547,7 @@ void LCodeGen::DoStackCheck(LStackCheck* instr) { __ cmp(sp, Operand(ip)); __ b(hs, &done); StackCheckStub stub; + PredictableCodeSizeScope predictable(masm_); CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); EnsureSpaceForLazyDeopt(); __ bind(&done); diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h index ac46f05..9281537 100644 --- a/src/arm/lithium-codegen-arm.h +++ b/src/arm/lithium-codegen-arm.h @@ -213,14 +213,18 @@ class LCodeGen BASE_EMBEDDED { RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS }; - void CallCode(Handle code, - RelocInfo::Mode mode, - LInstruction* instr); - - void CallCodeGeneric(Handle code, - RelocInfo::Mode mode, - LInstruction* instr, - SafepointMode safepoint_mode); + void CallCode( + Handle code, + RelocInfo::Mode mode, + LInstruction* instr, + TargetAddressStorageMode storage_mode = CAN_INLINE_TARGET_ADDRESS); + + void CallCodeGeneric( + Handle code, + RelocInfo::Mode mode, + LInstruction* instr, + SafepointMode safepoint_mode, + TargetAddressStorageMode storage_mode = CAN_INLINE_TARGET_ADDRESS); void CallRuntime(const Runtime::Function* function, int num_arguments, diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 4409e26..3901f48 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -108,7 +108,7 @@ void MacroAssembler::Jump(Handle 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 @@ -158,15 +158,29 @@ int MacroAssembler::CallSizeNotPredictableCodeSize( 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 @@ -177,15 +191,16 @@ void MacroAssembler::Call(Address target, mov(ip, Operand(reinterpret_cast(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 pc, [pc + offset of target in constant pool]. mov(pc, Operand(reinterpret_cast(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); + } } @@ -200,7 +215,8 @@ int MacroAssembler::CallSize(Handle code, void MacroAssembler::Call(Handle code, RelocInfo::Mode rmode, TypeFeedbackId ast_id, - Condition cond) { + Condition cond, + TargetAddressStorageMode mode) { Label start; bind(&start); ASSERT(RelocInfo::IsCodeTarget(rmode)); @@ -209,9 +225,7 @@ void MacroAssembler::Call(Handle code, rmode = RelocInfo::CODE_TARGET_WITH_ID; } // 'code' is always generated ARM code, never THUMB code - Call(reinterpret_cast
(code.location()), rmode, cond); - ASSERT_EQ(CallSize(code, rmode, ast_id, cond), - SizeOfCodeGeneratedSince(&start)); + Call(reinterpret_cast
(code.location()), rmode, cond, mode); } @@ -288,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(this) && + !src2.must_output_reloc_info(this) && src2.immediate() == 0) { mov(dst, Operand(0, RelocInfo::NONE), LeaveCC, cond); - } else if (!src2.is_single_instruction(this) && - !src2.must_use_constant_pool(this) && + !src2.must_output_reloc_info(this) && CpuFeatures::IsSupported(ARMv7) && IsPowerOf2(src2.immediate() + 1)) { ubfx(dst, src1, 0, WhichPowerOf2(static_cast(src2.immediate()) + 1), cond); - } else { and_(dst, src1, src2, LeaveCC, cond); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index c7032ba..1fcf6ba 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -102,6 +102,11 @@ bool AreAliased(Register reg1, #endif +enum TargetAddressStorageMode { + CAN_INLINE_TARGET_ADDRESS, + NEVER_INLINE_TARGET_ADDRESS +}; + // MacroAssembler implements a collection of frequently used macros. class MacroAssembler: public Assembler { public: @@ -121,7 +126,9 @@ class MacroAssembler: public Assembler { static int CallSizeNotPredictableCodeSize(Address target, RelocInfo::Mode rmode, Condition cond = al); - void Call(Address target, RelocInfo::Mode rmode, Condition cond = al); + void Call(Address target, RelocInfo::Mode rmode, + Condition cond = al, + TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS); int CallSize(Handle code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, TypeFeedbackId ast_id = TypeFeedbackId::None(), @@ -129,7 +136,8 @@ class MacroAssembler: public Assembler { void Call(Handle code, RelocInfo::Mode rmode = RelocInfo::CODE_TARGET, TypeFeedbackId ast_id = TypeFeedbackId::None(), - Condition cond = al); + Condition cond = al, + TargetAddressStorageMode mode = CAN_INLINE_TARGET_ADDRESS); void Ret(Condition cond = al); // Emit code to discard a non-negative number of pointer-sized elements diff --git a/src/assembler.h b/src/assembler.h index 0bf28ae..a0e55cc 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -368,19 +368,17 @@ class RelocInfo BASE_EMBEDDED { Mode rmode_; intptr_t data_; Code* host_; -#ifdef V8_TARGET_ARCH_MIPS - // Code and Embedded Object pointers in mips are stored split + // Code and Embedded Object pointers on some platforms are stored split // across two consecutive 32-bit instructions. Heap management // routines expect to access these pointers indirectly. The following - // location provides a place for these pointers to exist natually + // location provides a place for these pointers to exist naturally // when accessed via the Iterator. Object* reconstructed_obj_ptr_; // External-reference pointers are also split across instruction-pairs - // in mips, but are accessed via indirect pointers. This location + // on some platforms, but are accessed via indirect pointers. This location // provides a place for that pointer to exist naturally. Its address // is returned by RelocInfo::target_reference_address(). Address reconstructed_adr_ptr_; -#endif // V8_TARGET_ARCH_MIPS friend class RelocIterator; }; diff --git a/src/debug.cc b/src/debug.cc index cb2501e..48c5519 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -2285,7 +2285,7 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { // Find the call address in the running code. This address holds the call to // either a DebugBreakXXX or to the debug break return entry code if the // break point is still active after processing the break point. - Address addr = frame->pc() - Assembler::kCallTargetAddressOffset; + Address addr = frame->pc() - Assembler::kPatchDebugBreakSlotReturnOffset; // Check if the location is at JS exit or debug break slot. bool at_js_return = false; @@ -2374,7 +2374,7 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { #endif // Find the call address in the running code. - Address addr = frame->pc() - Assembler::kCallTargetAddressOffset; + Address addr = frame->pc() - Assembler::kPatchDebugBreakSlotReturnOffset; // Check if the location is at JS return. RelocIterator it(debug_info->code()); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index f040247..4c7c090 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -288,6 +288,9 @@ DEFINE_bool(enable_armv7, true, "enable use of ARMv7 instructions if available (ARM only)") DEFINE_bool(enable_sudiv, true, "enable use of SDIV and UDIV instructions if available (ARM only)") +DEFINE_bool(enable_movw_movt, false, + "enable loading 32-bit constant by means of movw/movt " + "instruction pairs (ARM only)") DEFINE_bool(enable_unaligned_accesses, true, "enable unaligned accesses for ARMv7 (ARM only)") DEFINE_bool(enable_fpu, true, diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h index 0b47748..7fdf50c 100644 --- a/src/ia32/assembler-ia32-inl.h +++ b/src/ia32/assembler-ia32-inl.h @@ -387,6 +387,11 @@ void Assembler::set_target_address_at(Address pc, Address target) { } +Address Assembler::target_address_from_return_address(Address pc) { + return pc - kCallTargetAddressOffset; +} + + Displacement Assembler::disp_at(Label* L) { return Displacement(long_at(L->pos())); } diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 4d9562e..b0f4651 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -601,6 +601,10 @@ class Assembler : public AssemblerBase { inline static Address target_address_at(Address pc); inline static void set_target_address_at(Address pc, Address target); + // Return the code target address at a call site from the return address + // of that call in the instruction stream. + inline static Address target_address_from_return_address(Address pc); + // This sets the branch destination (which is in the instruction on x86). // This is for calls and branches within generated code. inline static void deserialization_set_special_target_at( @@ -629,6 +633,7 @@ class Assembler : public AssemblerBase { static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32. static const int kCallInstructionLength = 5; + static const int kPatchDebugBreakSlotReturnOffset = kPointerSize; static const int kJSReturnSequenceLength = 6; // The debug break slot must be able to contain a call instruction. diff --git a/src/ic-inl.h b/src/ic-inl.h index 0e41093..49b6ef9 100644 --- a/src/ic-inl.h +++ b/src/ic-inl.h @@ -40,7 +40,7 @@ namespace internal { Address IC::address() const { // Get the address of the call. - Address result = pc() - Assembler::kCallTargetAddressOffset; + Address result = Assembler::target_address_from_return_address(pc()); #ifdef ENABLE_DEBUGGER_SUPPORT Debug* debug = Isolate::Current()->debug(); diff --git a/src/ic.cc b/src/ic.cc index b902b53..dd0bb10 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -158,7 +158,7 @@ Address IC::OriginalCodeAddress() const { // Get the address of the call site in the active code. This is the // place where the call to DebugBreakXXX is and where the IC // normally would be. - Address addr = pc() - Assembler::kCallTargetAddressOffset; + Address addr = Assembler::target_address_from_return_address(pc()); // Return the address in the original code. This is the place where // the call which has been overwritten by the DebugBreakXXX resides // and the place where the inline cache system should look. diff --git a/src/mark-compact.cc b/src/mark-compact.cc index 7a25855..24730c6 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -2327,8 +2327,11 @@ class PointersUpdatingVisitor: public ObjectVisitor { void VisitCodeTarget(RelocInfo* rinfo) { ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode())); Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); + Object* old_target = target; VisitPointer(&target); - rinfo->set_target_address(Code::cast(target)->instruction_start()); + if (target != old_target) { + rinfo->set_target_address(Code::cast(target)->instruction_start()); + } } void VisitDebugTarget(RelocInfo* rinfo) { diff --git a/src/platform-linux.cc b/src/platform-linux.cc index b9ce9d9..beb2cce 100644 --- a/src/platform-linux.cc +++ b/src/platform-linux.cc @@ -174,6 +174,24 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) { } +CpuImplementer OS::GetCpuImplementer() { + static bool use_cached_value = false; + static CpuImplementer cached_value = UNKNOWN_IMPLEMENTER; + if (use_cached_value) { + return cached_value; + } + if (CPUInfoContainsString("CPU implementer\t: 0x41")) { + cached_value = ARM_IMPLEMENTER; + } else if (CPUInfoContainsString("CPU implementer\t: 0x51")) { + cached_value = QUALCOMM_IMPLEMENTER; + } else { + cached_value = UNKNOWN_IMPLEMENTER; + } + use_cached_value = true; + return cached_value; +} + + bool OS::ArmUsingHardFloat() { // GCC versions 4.6 and above define __ARM_PCS or __ARM_PCS_VFP to specify // the Floating Point ABI used (PCS stands for Procedure Call Standard). diff --git a/src/platform-nullos.cc b/src/platform-nullos.cc index 679ef8e..7aaa7b2 100644 --- a/src/platform-nullos.cc +++ b/src/platform-nullos.cc @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -215,6 +215,11 @@ double OS::nan_value() { } +CpuImplementer OS::GetCpuImplementer() { + UNIMPLEMENTED(); +} + + bool OS::ArmCpuHasFeature(CpuFeature feature) { UNIMPLEMENTED(); } diff --git a/src/platform.h b/src/platform.h index f50e713..de896ac 100644 --- a/src/platform.h +++ b/src/platform.h @@ -308,6 +308,9 @@ class OS { // Returns the double constant NAN static double nan_value(); + // Support runtime detection of Cpu implementer + static CpuImplementer GetCpuImplementer(); + // Support runtime detection of VFP3 on ARM CPUs. static bool ArmCpuHasFeature(CpuFeature feature); diff --git a/src/v8globals.h b/src/v8globals.h index 648413f..95390ad 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -412,6 +412,13 @@ enum StateTag { #endif +enum CpuImplementer { + UNKNOWN_IMPLEMENTER, + ARM_IMPLEMENTER, + QUALCOMM_IMPLEMENTER +}; + + // Feature flags bit positions. They are mostly based on the CPUID spec. // (We assign CPUID itself to one of the currently reserved bits -- // feel free to change this if needed.) @@ -427,6 +434,7 @@ enum CpuFeature { SSE4_1 = 32 + 19, // x86 VFP2 = 3, // ARM SUDIV = 4, // ARM UNALIGNED_ACCESSES = 5, // ARM + MOVW_MOVT_IMMEDIATE_LOADS = 6, // ARM SAHF = 0, // x86 FPU = 1}; // MIPS diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h index f3940e8..d022340 100644 --- a/src/x64/assembler-x64-inl.h +++ b/src/x64/assembler-x64-inl.h @@ -195,6 +195,12 @@ void Assembler::set_target_address_at(Address pc, Address target) { CPU::FlushICache(pc, sizeof(int32_t)); } + +Address Assembler::target_address_from_return_address(Address pc) { + return pc - kCallTargetAddressOffset; +} + + Handle Assembler::code_target_object_handle_at(Address pc) { return code_targets_[Memory::int32_at(pc)]; } diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index e00b403..e8b0be9 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -581,6 +581,10 @@ class Assembler : public AssemblerBase { static inline Address target_address_at(Address pc); static inline void set_target_address_at(Address pc, Address target); + // Return the code target address at a call site from the return address + // of that call in the instruction stream. + static inline Address target_address_from_return_address(Address pc); + // This sets the branch destination (which is in the instruction on x64). // This is for calls and branches within generated code. inline static void deserialization_set_special_target_at( @@ -620,6 +624,7 @@ class Assembler : public AssemblerBase { static const int kCallInstructionLength = 13; static const int kJSReturnSequenceLength = 13; static const int kShortCallInstructionLength = 5; + static const int kPatchDebugBreakSlotReturnOffset = 4; // The debug break slot must be able to contain a call instruction. static const int kDebugBreakSlotLength = kCallInstructionLength; -- 2.7.4