Index: src/arm/assembler-arm.cc |
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc |
index ccf7208a8f6aa6c56cb73483d66a2a3ca1cca79d..cc3d5b11a323801f69ecdbc630b37a54a932c74e 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<uint32_t>(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<uint32_t>(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<SBit>(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()); |
} |