Index: src/sh4/assembler-sh4-inl.h |
diff --git a/src/sh4/assembler-sh4-inl.h b/src/sh4/assembler-sh4-inl.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..22698ce9a13eb62a20c969bd34ab1ec2e128a068 |
--- /dev/null |
+++ b/src/sh4/assembler-sh4-inl.h |
@@ -0,0 +1,508 @@ |
+// Copyright 2011-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: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following |
+// disclaimer in the documentation and/or other materials provided |
+// with the distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived |
+// from this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+#ifndef V8_SH4_ASSEMBLER_SH4_INL_H_ |
+#define V8_SH4_ASSEMBLER_SH4_INL_H_ |
+ |
+#include "sh4/assembler-sh4.h" |
+#include "sh4/checks-sh4.h" |
+#include "cpu.h" |
+#include "debug.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+void Assembler::CheckBuffer() { |
+ if (buffer_space() <= kGap) { |
+ GrowBuffer(); |
+ } |
+ // FIXME(STM): check if we must emit the constant pool |
+} |
+ |
+ |
+// The modes possibly affected by apply must be in kApplyMask. |
+void RelocInfo::apply(intptr_t delta) { |
+ if (RelocInfo::IsInternalReference(rmode_)) { |
+ // absolute code pointer inside code object moves with the code object. |
+ int32_t* p = reinterpret_cast<int32_t*>(pc_); |
+ *p += delta; // relocate entry |
+ } |
+ // We do not use pc relative addressing on ARM, so there is |
+ // nothing else to do. |
+} |
+ |
+ |
+Address RelocInfo::target_address() { |
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); |
+ return Assembler::target_address_at(pc_); |
+} |
+ |
+ |
+Address RelocInfo::target_address_address() { |
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY |
+ || rmode_ == EMBEDDED_OBJECT |
+ || rmode_ == EXTERNAL_REFERENCE); |
+ return reinterpret_cast<Address>(Assembler::target_pointer_address_at(pc_)); |
+} |
+ |
+ |
+int RelocInfo::target_address_size() { |
+ return kPointerSize; |
+} |
+ |
+ |
+void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { |
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); |
+ Assembler::set_target_address_at(pc_, target); |
+ if (mode == UPDATE_WRITE_BARRIER && host() != NULL && IsCodeTarget(rmode_)) { |
+ Object* target_code = Code::GetCodeFromTargetAddress(target); |
+ host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( |
+ host(), this, HeapObject::cast(target_code)); |
+ } |
+} |
+ |
+ |
+Object* RelocInfo::target_object() { |
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); |
+ return reinterpret_cast<Object*>(Assembler::target_pointer_at(pc_)); |
+} |
+ |
+ |
+Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { |
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); |
+ return Memory::Object_Handle_at(Assembler::target_pointer_address_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); |
+ reconstructed_obj_ptr_ = |
+ reinterpret_cast<Object*>(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_pointer_at(pc_, reinterpret_cast<Address>(target)); |
+ if (mode == UPDATE_WRITE_BARRIER && |
+ host() != NULL && |
+ target->IsHeapObject()) { |
+ host()->GetHeap()->incremental_marking()->RecordWrite( |
+ host(), &Memory::Object_at(pc_), HeapObject::cast(target)); |
+ } |
+} |
+ |
+ |
+Address* RelocInfo::target_reference_address() { |
+ ASSERT(rmode_ == EXTERNAL_REFERENCE); |
+ reconstructed_adr_ptr_ = Assembler::target_address_at(pc_); |
+ return &reconstructed_adr_ptr_; |
+} |
+ |
+ |
+Handle<JSGlobalPropertyCell> RelocInfo::target_cell_handle() { |
+ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); |
+ Address address = Memory::Address_at(pc_); |
+ return Handle<JSGlobalPropertyCell>( |
+ reinterpret_cast<JSGlobalPropertyCell**>(address)); |
+} |
+ |
+ |
+JSGlobalPropertyCell* RelocInfo::target_cell() { |
+ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); |
+ return JSGlobalPropertyCell::FromValueAddress(Memory::Address_at(pc_)); |
+} |
+ |
+ |
+void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, |
+ WriteBarrierMode mode) { |
+ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); |
+ Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; |
+ Memory::Address_at(pc_) = address; |
+ if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { |
+ // TODO(1550) We are passing NULL as a slot because cell can never be on |
+ // evacuation candidate. |
+ host()->GetHeap()->incremental_marking()->RecordWrite( |
+ host(), NULL, cell); |
+ } |
+ CPU::FlushICache(pc_, sizeof(Address)); |
+} |
+ |
+ |
+Address RelocInfo::call_address() { |
+ UNIMPLEMENTED(); |
+ return NULL; |
+} |
+ |
+ |
+void RelocInfo::set_call_address(Address target) { |
+ UNIMPLEMENTED(); |
+} |
+ |
+ |
+Object* RelocInfo::call_object() { |
+ return *call_object_address(); |
+} |
+ |
+ |
+void RelocInfo::set_call_object(Object* target) { |
+ *call_object_address() = target; |
+} |
+ |
+ |
+Object** RelocInfo::call_object_address() { |
+ UNIMPLEMENTED(); |
+ return NULL; |
+} |
+ |
+ |
+bool RelocInfo::IsPatchedReturnSequence() { |
+ UNIMPLEMENTED(); |
+ return false; |
+} |
+ |
+ |
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() { |
+ UNIMPLEMENTED(); |
+ return false; |
+} |
+ |
+ |
+void RelocInfo::Visit(ObjectVisitor* visitor) { |
+ RelocInfo::Mode mode = rmode(); |
+ if (mode == RelocInfo::EMBEDDED_OBJECT) { |
+ visitor->VisitEmbeddedPointer(this); |
+ CPU::FlushICache(pc_, sizeof(Address)); |
+ } else if (RelocInfo::IsCodeTarget(mode)) { |
+ visitor->VisitCodeTarget(this); |
+ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { |
+ visitor->VisitGlobalPropertyCell(this); |
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { |
+ visitor->VisitExternalReference(this); |
+ CPU::FlushICache(pc_, sizeof(Address)); |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ // TODO(isolates): Get a cached isolate below. |
+ } else if (((RelocInfo::IsJSReturn(mode) && |
+ IsPatchedReturnSequence()) || |
+ (RelocInfo::IsDebugBreakSlot(mode) && |
+ IsPatchedDebugBreakSlotSequence())) && |
+ Isolate::Current()->debug()->has_break_points()) { |
+ visitor->VisitDebugTarget(this); |
+#endif |
+ } else if (mode == RelocInfo::RUNTIME_ENTRY) { |
+ visitor->VisitRuntimeEntry(this); |
+ } |
+} |
+ |
+ |
+template<typename StaticVisitor> |
+void RelocInfo::Visit(Heap* heap) { |
+ RelocInfo::Mode mode = rmode(); |
+ if (mode == RelocInfo::EMBEDDED_OBJECT) { |
+ StaticVisitor::VisitEmbeddedPointer(heap, this); |
+ CPU::FlushICache(pc_, sizeof(Address)); |
+ } else if (RelocInfo::IsCodeTarget(mode)) { |
+ StaticVisitor::VisitCodeTarget(heap, this); |
+ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { |
+ StaticVisitor::VisitGlobalPropertyCell(heap, this); |
+ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { |
+ StaticVisitor::VisitExternalReference(this); |
+ CPU::FlushICache(pc_, sizeof(Address)); |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ } else if (heap->isolate()->debug()->has_break_points() && |
+ ((RelocInfo::IsJSReturn(mode) && |
+ IsPatchedReturnSequence()) || |
+ (RelocInfo::IsDebugBreakSlot(mode) && |
+ IsPatchedDebugBreakSlotSequence()))) { |
+ StaticVisitor::VisitDebugTarget(heap, this); |
+#endif |
+ } else if (mode == RelocInfo::RUNTIME_ENTRY) { |
+ StaticVisitor::VisitRuntimeEntry(this); |
+ } |
+} |
+ |
+ |
+Operand::Operand(int32_t immediate, RelocInfo::Mode rmode) { |
+ imm32_ = immediate; |
+ rmode_ = rmode; |
+} |
+ |
+ |
+Operand::Operand(const ExternalReference& f) { |
+ imm32_ = reinterpret_cast<int32_t>(f.address()); |
+ rmode_ = RelocInfo::EXTERNAL_REFERENCE; |
+} |
+ |
+ |
+Operand::Operand(Smi* value) { |
+ imm32_ = reinterpret_cast<intptr_t>(value); |
+ rmode_ = RelocInfo::NONE; |
+} |
+ |
+ |
+MemOperand::MemOperand(Register Rx, int32_t offset, AddrMode mode) { |
+ rm_ = Rx; |
+ rn_ = no_reg; |
+ offset_ = offset; |
+ mode_ = mode; |
+} |
+ |
+ |
+MemOperand::MemOperand(Register Rd, Register offset) { |
+ rm_ = Rd; |
+ rn_ = offset; |
+ offset_ = 0; |
+ mode_ = Offset; |
+} |
+ |
+ |
+Address Assembler::target_pointer_address_at(Address pc) { |
+ // Compute the actual address in the code where the address of the |
+ // jump/call/mov instruction is stored given the instruction pc. |
+ // Ref to functions that call Assembler::RecordRelocInfo() |
+ // such as Assembler::mov(), Assembler::jmp(), such as Assembler::jsr(). |
+ |
+ // All sequences for jmp/jsr/mov uses the same sequence as mov(), i.e.: |
+ // align 4; |
+ // movl pc+4 => R; nop; bra pc+4; nop; pool[0..32] |
+ // We compute the address of pool[0] given the pc address after the align |
+ Address pool_address = pc; |
+ ASSERT(IsMovlPcRelative(instr_at(pc))); // check if 'movl disp, pc' |
+ ASSERT(reinterpret_cast<uint32_t>(pc) % 4 == 0); // check after align |
+ pool_address += 4 * kInstrSize; |
+ return pool_address; |
+} |
+ |
+ |
+Address Assembler::target_pointer_at(Address pc) { |
+ 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. |
+ UNIMPLEMENTED(); |
+ return NULL; |
+} |
+ |
+Address Assembler::target_address_at(Address pc) { |
+ return target_pointer_at(pc); |
+} |
+ |
+ |
+void Assembler::set_target_pointer_at(Address pc, Address target) { |
+ 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 SH4, no instruction is actually patched in the case |
+ // of embedded constants. |
+} |
+ |
+ |
+void Assembler::set_target_address_at(Address pc, Address target) { |
+ set_target_pointer_at(pc, target); |
+} |
+ |
+ |
+Address Assembler::return_address_from_call_start(Address pc) { |
+ UNIMPLEMENTED(); |
+ return NULL; |
+} |
+ |
+ |
+int Assembler::align() { |
+ int count = 0; |
+ while (((unsigned)pc_ & 0x3) != 0) { |
+ nop_(); |
+ count++; |
+ } |
+ return count; |
+} |
+ |
+ |
+int Assembler::misalign() { |
+ int count = 0; |
+ while (((unsigned)pc_ & 0x3) != 2) { |
+ nop_(); |
+ count++; |
+ } |
+ return count; |
+} |
+ |
+ |
+void Assembler::cmp(Condition *cond, Register Rd, Register Rs) { |
+ Condition cond_to_test = eq; |
+ switch (*cond) { |
+ case ne: |
+ cond_to_test = ne; |
+ case eq: |
+ cmpeq(Rd, Rs); |
+ break; |
+ |
+ case lt: |
+ cond_to_test = ne; |
+ case ge: |
+ cmpge(Rd, Rs); |
+ break; |
+ |
+ case le: |
+ cond_to_test = ne; |
+ case gt: |
+ cmpgt(Rd, Rs); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ *cond = cond_to_test; |
+} |
+ |
+ |
+void Assembler::cmpeq(Register Rd, const Operand& imm, Register rtmp) { |
+ if (Rd.is(r0) && FITS_SH4_cmpeq_imm_R0(imm.imm32_)) { |
+ cmpeq_imm_R0_(imm.imm32_); |
+ } else { |
+ mov(rtmp, imm); |
+ cmpeq_(rtmp, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::cmpgt(Register Rd, const Operand& imm, Register rtmp) { |
+ mov(rtmp, imm); |
+ cmpgt_(rtmp, Rd); |
+} |
+ |
+ |
+void Assembler::cmpge(Register Rd, const Operand& imm, Register rtmp) { |
+ mov(rtmp, imm); |
+ cmpge_(rtmp, Rd); |
+} |
+ |
+ |
+void Assembler::cmphi(Register Rd, const Operand& imm, Register rtmp) { |
+ mov(rtmp, imm); |
+ cmphi_(rtmp, Rd); |
+} |
+ |
+ |
+void Assembler::cmphs(Register Rd, const Operand& imm, Register rtmp) { |
+ mov(rtmp, imm); |
+ cmphs_(rtmp, Rd); |
+} |
+ |
+ |
+void Assembler::emit(Instr x) { |
+ CheckBuffer(); |
+ *reinterpret_cast<uint16_t*>(pc_) = x; |
+ pc_ += sizeof(uint16_t); |
+} |
+ |
+ |
+void Assembler::rsb(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ if (imm.imm32_ == 0 && imm.rmode_ == RelocInfo::NONE) { |
+ neg_(Rs, Rd); |
+ } else { |
+ mov(rtmp, imm); |
+ sub(Rd, rtmp, Rs); |
+ } |
+} |
+ |
+void Assembler::rsb(Register Rd, Register Rs, Register Rt) { |
+ sub(Rd, Rt, Rs); |
+} |
+ |
+ |
+void Assembler::rsb(Register Rd, Register Rs, const Operand& imm, |
+ Condition cond, Register rtmp) { |
+ ASSERT(cond == ne || cond == eq); |
+ if (imm.imm32_ == 0 && imm.rmode_ == RelocInfo::NONE) { |
+ if (cond == eq) |
+ bf_(0); // Jump after sequence if T bit is false |
+ else |
+ bt_(0); // Jump after sequence if T bit is true |
+ neg_(Rs, Rd); |
+ } else { |
+ Label end; |
+ if (cond == eq) |
+ bf_near(&end); // Jump after sequence if T bit is false |
+ else |
+ bt_near(&end); // Jump after sequence if T bit is true |
+ rsb(Rd, Rs, imm, rtmp); |
+ bind(&end); |
+ } |
+} |
+ |
+ |
+void Assembler::rts() { |
+ rts_(); |
+ nop_(); |
+} |
+ |
+ |
+void Assembler::ldr(Register Rd, const MemOperand& src, Register rtmp) { |
+ switch (src.mode_) { |
+ case PreIndex: |
+ add(src.rm_ , src.rm_, Operand(src.offset()), rtmp); |
+ mov(Rd, MemOperand(src.rm_, 0), rtmp); |
+ break; |
+ case PostIndex: |
+ mov(Rd, MemOperand(src.rm_, 0), rtmp); |
+ add(src.rm_, src.rm_, Operand(src.offset()), rtmp); |
+ break; |
+ case Offset: |
+ mov(Rd, src, rtmp); |
+ break; |
+ } |
+} |
+ |
+ |
+void Assembler::str(Register Rs, const MemOperand& dst, Register rtmp) { |
+ switch (dst.mode_) { |
+ case PreIndex: |
+ add(dst.rm_ , dst.rm_, Operand(dst.offset()), rtmp); |
+ mov(MemOperand(dst.rm_, 0), Rs, rtmp); |
+ break; |
+ case PostIndex: |
+ mov(MemOperand(dst.rm_, 0), Rs, rtmp); |
+ add(dst.rm_, dst.rm_, Operand(dst.offset()), rtmp); |
+ break; |
+ case Offset: |
+ mov(dst, Rs, rtmp); |
+ break; |
+ } |
+} |
+ |
+ |
+} } // namespace v8::internal |
+ |
+#endif // V8_SH4_ASSEMBLER_SH4_INL_H_ |