Index: src/arm/assembler-arm-inl.h |
diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h |
index 41287e64d9f2c9a85031a978f0349ce6bb86ad1f..84fcf289e58c9a2fa2c4edebb44a38a800c1ab4e 100644 |
--- a/src/arm/assembler-arm-inl.h |
+++ b/src/arm/assembler-arm-inl.h |
@@ -155,7 +155,9 @@ void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { |
host() != NULL && |
target->IsHeapObject()) { |
host()->GetHeap()->incremental_marking()->RecordWrite( |
- host(), &Memory::Object_at(pc_), HeapObject::cast(target)); |
+ host(), |
+ &Memory::Object_at(Assembler::UntagAddress(pc_)), |
+ HeapObject::cast(target)); |
} |
} |
@@ -380,6 +382,10 @@ void Assembler::CheckBuffer() { |
void Assembler::emit(Instr x) { |
+ if (is_thumb_mode()) { |
+ emit32(x); |
+ return; |
+ } |
CheckBuffer(); |
*reinterpret_cast<Instr*>(pc_) = x; |
pc_ += kInstrSize; |
@@ -403,6 +409,10 @@ void Assembler::emit32(Instr x) { |
Address Assembler::target_pointer_address_at(Address pc) { |
+ if (IsThumbAddress(pc)) { |
+ return thumb_target_pointer_address_at(UntagAddress(pc)); |
+ } |
+ |
Address target_pc = pc; |
Instr instr = Memory::int32_at(target_pc); |
// If we have a bx instruction, the instruction before the bx is |
@@ -430,6 +440,9 @@ Address Assembler::target_pointer_address_at(Address pc) { |
Address Assembler::target_pointer_at(Address pc) { |
+ if (IsThumbAddress(pc)) { |
+ return thumb_target_pointer_at(UntagAddress(pc)); |
+ } |
if (IsMovW(Memory::int32_at(pc))) { |
ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); |
Instruction* instr = Instruction::At(pc); |
@@ -458,6 +471,17 @@ Address Assembler::target_address_from_return_address(Address pc) { |
Instr candidate_instr(Memory::int32_at(candidate)); |
if (IsLdrPcImmediateOffset(candidate_instr)) { |
return candidate; |
+ } else { |
+ candidate = pc - Assembler::kInstrSize - Assembler::kInstr16Size - 1; |
+ if (IsThumbLdrPcImmediateOffset(thumb32_instr_at(candidate))) { |
+ return candidate + 1; |
+ } else { |
+ candidate = pc - 2 * Assembler::kInstrSize - Assembler::kInstr16Size - 1; |
+ if (IsMovWThumb(thumb32_instr_at(candidate))) { |
+ ASSERT(IsMovTThumb(thumb32_instr_at(candidate + kInstrSize))); |
+ return candidate + 1; |
+ } |
+ } |
} |
candidate = pc - 3 * Assembler::kInstrSize; |
ASSERT(IsMovW(Memory::int32_at(candidate)) && |
@@ -467,6 +491,16 @@ Address Assembler::target_address_from_return_address(Address pc) { |
Address Assembler::return_address_from_call_start(Address pc) { |
+ if (IsThumbAddress(pc)) { |
+ pc = UntagAddress(pc); |
+ Instr instr = thumb32_instr_at(pc); |
+ if (IsThumbLdrPcImmediateOffset(instr)) { |
+ return pc + kInstrSize + kInstr16Size; |
+ } |
+ ASSERT(IsMovWThumb(instr)); |
+ ASSERT(IsMovTThumb(thumb32_instr_at(pc + kInstrSize))); |
+ return pc + kInstrSize * 2 + kInstr16Size; |
+ } |
if (IsLdrPcImmediateOffset(Memory::int32_at(pc))) { |
return pc + kInstrSize * 2; |
} else { |
@@ -496,6 +530,10 @@ static Instr EncodeMovwImmediate(uint32_t immediate) { |
void Assembler::set_target_pointer_at(Address pc, Address target) { |
+ if (IsThumbAddress(pc)) { |
+ thumb_set_target_pointer_at(UntagAddress(pc), target); |
+ return; |
+ } |
if (IsMovW(Memory::int32_at(pc))) { |
ASSERT(IsMovT(Memory::int32_at(pc + kInstrSize))); |
uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc); |
@@ -542,6 +580,76 @@ void Assembler::emit_it(Condition cond) { |
} |
} |
+ |
+Address Assembler::AlignAddress(Address addr) { |
+ return (Address)((uint32_t)addr &(~3)); |
+} |
+ |
+ |
+Address Assembler::UntagAddress(Address addr) { |
+ return (Address)((uint32_t)addr & (~1)); |
+} |
+ |
+ |
+bool Assembler::IsThumbAddress(Address addr) { |
+ return (((uint32_t)addr) & 1) == 1; |
+} |
+ |
+ |
+Address Assembler::thumb_target_pointer_at(Address pc) { |
+ Instr instr = thumb32_instr_at(pc); |
+ if (IsMovWThumb(instr)) { |
+ Instr next_instr = thumb32_instr_at(pc + kInstrSize); |
+ ASSERT(IsMovTThumb(next_instr)); |
+ return reinterpret_cast<Address>( |
+ (thumb32_movw_immediate(next_instr) << 16) | |
+ thumb32_movw_immediate(instr)); |
+ } |
+ return Memory::Address_at(thumb_target_pointer_address_at(pc)); |
+} |
+ |
+ |
+Address Assembler::thumb_target_pointer_address_at(Address pc) { |
+ // We must have a load from the constant pool. The address in the constant |
+ // pool is what needs to be patched. |
+ Instr instr = thumb32_instr_at(pc); |
+ ASSERT(IsThumbLdrPcImmediateOffset(instr)); |
+ pc = AlignAddress(pc); |
+ |
+ int offset = instr & 0xfff; // offset_12 is unsigned |
+ if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign |
+ // Verify that the constant pool comes after the instruction referencing it. |
+ ASSERT(offset >= -4); |
+ return pc + offset + kThumbPcLoadDelta; |
+} |
+ |
+ |
+void Assembler::thumb_set_target_pointer_at(Address pc, |
+ Address target, |
+ Code* host) { |
+ Instr instr = thumb32_instr_at(pc); |
+ uint32_t immediate = reinterpret_cast<uint32_t>(target); |
+ if (IsMovWThumb(instr)) { |
+ Instr next_instr = thumb32_instr_at(pc + kInstrSize); |
+ ASSERT(IsMovTThumb(next_instr)); |
+ uint16_t* instr_ptr = reinterpret_cast<uint16_t*>(pc); |
+ uint32_t immediate_mask = thumb32_set_movw_immediate(0xFFFF); |
+ instr &= ~immediate_mask; |
+ instr |= thumb32_set_movw_immediate(immediate & 0xFFFF); |
+ next_instr &= ~immediate_mask; |
+ next_instr |= thumb32_set_movw_immediate(immediate >> 16); |
+ instr_ptr[0] = instr >> 16; |
+ instr_ptr[1] = instr & 0xFFFF; |
+ instr_ptr[2] = next_instr >> 16; |
+ instr_ptr[3] = next_instr & 0xFFFF; |
+ CPU::FlushICache(pc, 2 * kInstrSize); |
+ return; |
+ } else if (IsThumbLdrPcImmediateOffset(instr)) { |
+ Memory::Address_at(thumb_target_pointer_address_at(pc)) = target; |
+ return; |
+ } |
+} |
+ |
} } // namespace v8::internal |
#endif // V8_ARM_ASSEMBLER_ARM_INL_H_ |