Index: src/sh4/assembler-sh4.cc |
diff --git a/src/sh4/assembler-sh4.cc b/src/sh4/assembler-sh4.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fa6139fae08d9536dd1f5d54472c78dd1c3211e9 |
--- /dev/null |
+++ b/src/sh4/assembler-sh4.cc |
@@ -0,0 +1,1602 @@ |
+// 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. |
+ |
+#include "v8.h" |
+ |
+#if defined(V8_TARGET_ARCH_SH4) |
+ |
+#include "sh4/assembler-sh4-inl.h" |
+#include "disassembler.h" |
+#include "macro-assembler.h" |
+#include "serialize.h" |
+ |
+#include "checks-sh4.h" |
+ |
+namespace v8 { |
+namespace internal { |
+ |
+#ifdef DEBUG |
+bool CpuFeatures::initialized_ = false; |
+#endif |
+unsigned CpuFeatures::supported_ = 0; |
+unsigned CpuFeatures::found_by_runtime_probing_ = 0; |
+ |
+ |
+// Get the CPU features enabled by the build. For cross compilation the |
+// preprocessor symbols CAN_USE_FPU_INSTRUCTIONS |
+// can be defined to enable FPU instructions when building the |
+// snapshot. |
+static unsigned CpuFeaturesImpliedByCompiler() { |
+ unsigned answer = 0; |
+#ifdef CAN_USE_FPU_INSTRUCTIONS |
+ answer |= 1u << FPU; |
+#endif // CAN_USE_FPU_INSTRUCTIONS |
+ |
+#ifdef __sh__ |
+ // If the compiler is allowed to use FPU then we can use FPU too in our code |
+ // generation even when generating snapshots. This won't work for cross |
+ // compilation. |
+#if(defined(__SH_FPU_ANY__) && __SH_FPU_ANY__ != 0) |
+ answer |= 1u << FPU; |
+#endif // defined(__SH_FPU_ANY__) && __SH_FPU_ANY__ != 0 |
+#endif // def __sh__ |
+ |
+ return answer; |
+} |
+ |
+ |
+void CpuFeatures::Probe() { |
+ unsigned standard_features = static_cast<unsigned>( |
+ OS::CpuFeaturesImpliedByPlatform()) | CpuFeaturesImpliedByCompiler(); |
+ ASSERT(supported_ == 0 || supported_ == standard_features); |
+#ifdef DEBUG |
+ initialized_ = true; |
+#endif |
+ |
+ // Get the features implied by the OS and the compiler settings. This is the |
+ // minimal set of features which is also alowed for generated code in the |
+ // snapshot. |
+ supported_ |= standard_features; |
+ |
+ if (Serializer::enabled()) { |
+ // No probing for features if we might serialize (generate snapshot). |
+ return; |
+ } |
+ |
+#ifndef __sh__ |
+ // For the simulator=sh4 build, use FPU when FLAG_enable_fpu is enabled. |
+ if (FLAG_enable_fpu) { |
+ supported_ |= 1u << FPU; |
+ } |
+#else // def __sh__ |
+ // Probe for additional features not already known to be available. |
+ if (!IsSupported(FPU) && OS::SHCpuHasFeature(FPU)) { |
+ // This implementation also sets the FPU flags if runtime |
+ // detection of FPU returns true. |
+ supported_ |= 1u << FPU; |
+ found_by_runtime_probing_ |= 1u << FPU; |
+ } |
+#endif |
+} |
+ |
+ |
+// ----------------------------------------------------------------------------- |
+// Implementation of Operand and MemOperand |
+// See assembler-sh4-inl.h for inlined constructors |
+ |
+Operand::Operand(Handle<Object> handle) { |
+ // Verify all Objects referred by code are NOT in new space. |
+ Object* obj = *handle; |
+ ASSERT(!HEAP->InNewSpace(obj)); |
+ if (obj->IsHeapObject()) { |
+ imm32_ = reinterpret_cast<intptr_t>(handle.location()); |
+ rmode_ = RelocInfo::EMBEDDED_OBJECT; |
+ } else { |
+ // no relocation needed |
+ imm32_ = reinterpret_cast<intptr_t>(obj); |
+ rmode_ = RelocInfo::NONE; |
+ } |
+} |
+ |
+ |
+void Assembler::memcpy(Register dst, Register src, Register count, |
+ Register scratch1, Register scratch2, |
+ Register scratch3, Register scratch4) { |
+ align(); |
+ mov_(r0, scratch4); |
+ |
+ mov_(dst, r0); |
+ add_(count, r0); |
+ |
+ sub_(dst, src); |
+ add_imm_(-1, src); |
+ |
+ shlr_(count); |
+ mov_(src, scratch3); |
+ |
+ movb_dispR0Rs_(src, scratch1); |
+ bfs_(8); // odd |
+ |
+ add_imm_(-1, scratch3); |
+ tst_(count, count); |
+ |
+ bts_(12); // end |
+ movb_decRd_(scratch1, r0); |
+ |
+ // even: |
+ movb_dispR0Rs_(src, scratch1); |
+ // odd: |
+ movb_dispR0Rs_(scratch3, scratch2); |
+ dt_(count); |
+ |
+ movb_decRd_(scratch1, r0); |
+ bfs_(-12); // even |
+ movb_decRd_(scratch2, r0); |
+ |
+ mov_(scratch4, r0); |
+} |
+ |
+void Assembler::memcmp(Register left, Register right, Register length, |
+ Register scratch1, Register scratch2, Label *not_equal) { |
+ Label loop; |
+ bind(&loop); |
+ movb_incRs_(left, scratch1); |
+ movb_incRs_(right, scratch2); |
+ cmpeq(scratch1, scratch2); |
+ bf(not_equal); |
+ dt_(length); |
+ bf(&loop); |
+} |
+ |
+void Assembler::Align(int m) { |
+ ASSERT(m >= 4 && IsPowerOf2(m)); |
+ while ((pc_offset() & (m - 1)) != 0) { |
+ nop_(); |
+ } |
+} |
+ |
+ |
+static const int kMinimalBufferSize = 4*KB; |
+ |
+ |
+Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size) |
+ : AssemblerBase(arg_isolate), |
+ recorded_ast_id_(TypeFeedbackId::None()), |
+ positions_recorder_(this), |
+ emit_debug_code_(FLAG_debug_code), |
+ predictable_code_size_(false) { |
+ if (buffer == NULL) { |
+ // Do our own buffer management. |
+ if (buffer_size <= kMinimalBufferSize) { |
+ buffer_size = kMinimalBufferSize; |
+ |
+ if (isolate()->assembler_spare_buffer() != NULL) { |
+ buffer = isolate()->assembler_spare_buffer(); |
+ isolate()->set_assembler_spare_buffer(NULL); |
+ } |
+ } |
+ if (buffer == NULL) { |
+ buffer_ = NewArray<byte>(buffer_size); |
+ } else { |
+ buffer_ = static_cast<byte*>(buffer); |
+ } |
+ buffer_size_ = buffer_size; |
+ own_buffer_ = true; |
+ |
+ } else { |
+ // Use externally provided buffer instead. |
+ ASSERT(buffer_size > 0); |
+ buffer_ = static_cast<byte*>(buffer); |
+ buffer_size_ = buffer_size; |
+ own_buffer_ = false; |
+ } |
+ |
+ // Fill the buffer with 0 so it will normally crash if we jump into it |
+ if (own_buffer_) { |
+ memset(buffer_, 0x00, buffer_size); |
+ } |
+ |
+ // Set up buffer pointers. |
+ ASSERT(buffer_ != NULL); |
+ pc_ = buffer_; |
+ reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); |
+ |
+ ClearRecordedAstId(); |
+} |
+ |
+ |
+void Assembler::GetCode(CodeDesc* desc) { |
+ // Emit the constant pool if needed |
+ // FIXME(STM) |
+ |
+ desc->buffer = buffer_; |
+ desc->buffer_size = buffer_size_; |
+ desc->instr_size = pc_offset(); |
+ desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos(); |
+ desc->origin = this; |
+} |
+ |
+// Debugging. |
+void Assembler::RecordJSReturn() { |
+ positions_recorder()->WriteRecordedPositions(); |
+ CheckBuffer(); |
+ RecordRelocInfo(RelocInfo::JS_RETURN); |
+} |
+ |
+void Assembler::RecordComment(const char* msg, bool force) { |
+ if (FLAG_code_comments) { |
+ CheckBuffer(); |
+ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg)); |
+ } |
+} |
+ |
+ |
+void Assembler::GrowBuffer() { |
+ if (!own_buffer_) FATAL("external code buffer is too small"); |
+ |
+ // Compute new buffer size. |
+ CodeDesc desc; // the new buffer |
+ if (buffer_size_ < 4*KB) { |
+ desc.buffer_size = 4*KB; |
+ } else if (buffer_size_ < 1*MB) { |
+ desc.buffer_size = 2*buffer_size_; |
+ } else { |
+ desc.buffer_size = buffer_size_ + 1*MB; |
+ } |
+ CHECK_GT(desc.buffer_size, 0); // no overflow |
+ |
+ // Setup new buffer. |
+ desc.buffer = NewArray<byte>(desc.buffer_size); |
+ |
+ desc.instr_size = pc_offset(); |
+ desc.reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos(); |
+ |
+ // Copy the data. |
+ int pc_delta = desc.buffer - buffer_; |
+ int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_); |
+ memmove(desc.buffer, buffer_, desc.instr_size); |
+ memmove(reloc_info_writer.pos() + rc_delta, |
+ reloc_info_writer.pos(), desc.reloc_size); |
+ |
+ // Switch buffers. |
+ DeleteArray(buffer_); |
+ buffer_ = desc.buffer; |
+ buffer_size_ = desc.buffer_size; |
+ pc_ += pc_delta; |
+ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, |
+ reloc_info_writer.last_pc() + pc_delta); |
+} |
+ |
+ |
+void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { |
+ ASSERT(rmode != RelocInfo::NONE); |
+ // Don't record external references unless the heap will be serialized. |
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) { |
+#ifdef DEBUG |
+ if (!Serializer::enabled()) { |
+ Serializer::TooLateToEnableNow(); |
+ } |
+#endif |
+ if (!Serializer::enabled() && !emit_debug_code()) { |
+ return; |
+ } |
+ } |
+ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) { |
+ RelocInfo reloc_info_with_ast_id(pc_, rmode, RecordedAstId().ToInt(), NULL); |
+ ClearRecordedAstId(); |
+ reloc_info_writer.Write(&reloc_info_with_ast_id); |
+ } else { |
+ // we do not try to reuse pool constants |
+ RelocInfo rinfo(pc_, rmode, data, NULL); |
+ reloc_info_writer.Write(&rinfo); |
+ } |
+} |
+ |
+ |
+void Assembler::add(Register Rd, const Operand& imm, Register rtmp) { |
+ if (imm.is_int8()) { |
+ add_imm_(imm.imm32_, Rd); |
+ } else { |
+ ASSERT(!Rd.is(rtmp)); |
+ mov(rtmp, imm); |
+ add_(rtmp, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::add(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ if (Rs.code() != Rd.code()) { |
+ mov(Rd, imm); |
+ add_(Rs, Rd); |
+ } else { |
+ add(Rd, imm, rtmp); |
+ } |
+} |
+ |
+ |
+void Assembler::add(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) |
+ add_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ add_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ add_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::sub(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ mov(rtmp, imm); |
+ if (Rs.code() == Rd.code()) { |
+ sub_(rtmp, Rd); |
+ } else { |
+ mov_(Rs, Rd); |
+ sub_(rtmp, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::sub(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) { |
+ sub_(Rt, Rd); |
+ } else if (Rt.code() == Rd.code()) { |
+ ASSERT(!Rs.is(Rd)); |
+ neg_(Rt, Rd); |
+ add_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ sub_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::addv(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) |
+ addv_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ addv_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ addv_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::addv(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ mov(rtmp, imm); |
+ addv(Rd, Rs, rtmp); |
+} |
+ |
+void Assembler::addc(Register Rd, Register Rs, Register Rt) { |
+ // Clear T bit before using addc |
+ clrt_(); |
+ if (Rs.code() == Rd.code()) |
+ addc_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ addc_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ addc_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::subv(Register Rd, Register Rs, Register Rt, Register rtmp) { |
+ if (Rs.code() == Rd.code()) |
+ subv_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ ASSERT(!Rs.is(rtmp) && !Rt.is(rtmp)); |
+ mov_(Rs, rtmp); |
+ subv_(Rt, rtmp); |
+ mov_(rtmp, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ subv_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::subc(Register Rd, Register Rs, Register Rt, Register rtmp) { |
+ // Clear T bit before using subc |
+ clrt_(); |
+ if (Rs.code() == Rd.code()) |
+ subc_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ ASSERT(!Rs.is(rtmp) && !Rt.is(rtmp)); |
+ mov_(Rs, rtmp); |
+ subc_(Rt, rtmp); |
+ mov_(rtmp, Rd); |
+ } else { |
+ ASSERT(!Rs.is(Rd) && !Rt.is(Rd)); |
+ mov_(Rs, Rd); |
+ subc_(Rt, Rd); |
+ } |
+} |
+ |
+// TODO(stm): check why asl is useful? Is it like lsl? |
+void Assembler::asl(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ ASSERT(imm.imm32_ >= 0 && imm.imm32_ < 32); |
+ if (Rs.code() != Rd.code()) |
+ mov_(Rs, Rd); |
+ if (imm.imm32_ == 1) { |
+ shal_(Rd); |
+ } else { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp)); |
+ mov_imm_(imm.imm32_, rtmp); |
+ shad_(rtmp, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::asr(Register Rd, Register Rs, Register Rt, bool in_range, |
+ Register rtmp) { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp) && !Rt.is(rtmp)); |
+ // If !in_range, we must clamp shift value to 31 max |
+ if (!in_range) { |
+ movt_(rtmp); |
+ push(rtmp); |
+ cmphi(Rt, Operand(31), rtmp); |
+ } |
+ neg_(Rt, rtmp); |
+ if (!in_range) { |
+ bf_(0); |
+ mov_imm_(-31, rtmp); |
+ } |
+ if (Rs.code() != Rd.code()) { |
+ mov_(Rs, Rd); |
+ } |
+ shad_(rtmp, Rd); |
+ if (!in_range) { |
+ pop(rtmp); |
+ cmppl_(rtmp); // gives back t bit |
+ } |
+} |
+ |
+ |
+void Assembler::asr(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ ASSERT(imm.imm32_ >= 0 && imm.imm32_ < 32); |
+ if (Rs.code() != Rd.code()) |
+ mov_(Rs, Rd); |
+ if (imm.imm32_ == 1) { |
+ shar_(Rd); |
+ } else { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp)); |
+ mov_imm_(-imm.imm32_, rtmp); |
+ shad_(rtmp, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::lsl(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ ASSERT(imm.imm32_ >= 0 && imm.imm32_ < 32); |
+ if (Rs.code() != Rd.code()) |
+ mov_(Rs, Rd); |
+ if (imm.imm32_ == 1) { |
+ shll_(Rd); |
+ } else if (imm.imm32_ == 2) { |
+ shll2_(Rd); |
+ } else { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp)); |
+ mov_imm_(imm.imm32_, rtmp); |
+ shld_(rtmp, Rd); |
+ } |
+} |
+ |
+void Assembler::lsl(Register Rd, Register Rs, Register Rt, bool wrap, |
+ Register rtmp) { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp) && !Rt.is(rtmp)); |
+ Register rshift = Rt; |
+ // If !in_range, we must flush in case of shift value >= 32 |
+ if (!wrap) { |
+ movt_(rtmp); |
+ push(rtmp); |
+ cmphi(Rt, Operand(31), rtmp); |
+ } |
+ if (Rs.code() != Rd.code()) { |
+ if (Rt.is(Rd)) { |
+ rshift = rtmp; |
+ mov_(Rt, rtmp); |
+ } |
+ mov_(Rs, Rd); |
+ } |
+ shld_(rshift, Rd); |
+ if (!wrap) { |
+ bf_(0); |
+ // Nullify result for shift amount >= 32 |
+ mov_imm_(0, Rd); |
+ pop(rtmp); |
+ cmppl_(rtmp); // gives back t bit |
+ } |
+} |
+ |
+ |
+void Assembler::lsr(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ ASSERT(imm.imm32_ >= 0 && imm.imm32_ < 32); |
+ if (Rs.code() != Rd.code()) |
+ mov_(Rs, Rd); |
+ if (imm.imm32_ == 1) { |
+ shlr_(Rd); |
+ } else if (imm.imm32_ == 2) { |
+ shlr2_(Rd); |
+ } else { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp)); |
+ mov_imm_(-imm.imm32_, rtmp); |
+ shld_(rtmp, Rd); |
+ } |
+} |
+ |
+void Assembler::lsr(Register Rd, Register Rs, Register Rt, bool in_range, |
+ Register rtmp) { |
+ ASSERT(!Rs.is(rtmp) && !Rd.is(rtmp) && !Rt.is(rtmp)); |
+ // If !in_range, we must flush in case of shift value >= 32 |
+ if (!in_range) { |
+ movt_(rtmp); |
+ push(rtmp); |
+ cmphi(Rt, Operand(31), rtmp); |
+ } |
+ neg_(Rt, rtmp); |
+ if (Rs.code() != Rd.code()) { |
+ mov_(Rs, Rd); |
+ } |
+ shld_(rtmp, Rd); |
+ if (!in_range) { |
+ bf_(0); |
+ // Nullify result for shift amount >= 32 |
+ mov_imm_(0, Rd); |
+ pop(rtmp); |
+ cmppl_(rtmp); // gives back t bit |
+ } |
+} |
+ |
+ |
+void Assembler::land(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ if (Rd.is(r0) && Rd.is(Rs) && FITS_SH4_and_imm_R0(imm.imm32_)) { |
+ and_imm_R0_(imm.imm32_); |
+ } else { |
+ ASSERT(!Rs.is(rtmp)); |
+ mov(rtmp, imm); |
+ land(Rd, Rs, rtmp); |
+ } |
+} |
+ |
+ |
+void Assembler::land(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) |
+ and_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ and_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rt.is(Rd) && !Rs.is(Rd)); |
+ mov_(Rs, Rd); |
+ and_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::lor(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ if (Rd.is(r0) && Rd.is(Rs) && FITS_SH4_or_imm_R0(imm.imm32_)) { |
+ or_imm_R0_(imm.imm32_); |
+ } else { |
+ ASSERT(!Rs.is(rtmp)); |
+ mov(rtmp, imm); |
+ lor(Rd, Rs, rtmp); |
+ } |
+} |
+ |
+void Assembler::lor(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) |
+ or_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ or_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rt.is(Rd) && !Rs.is(Rd)); |
+ mov_(Rs, Rd); |
+ or_(Rt, Rd); |
+ } |
+} |
+ |
+void Assembler::lor(Register Rd, Register Rs, Register Rt, Condition cond) { |
+ ASSERT(cond == ne || cond == eq); |
+ 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 |
+ lor(Rd, Rs, Rt); |
+ bind(&end); |
+} |
+ |
+ |
+void Assembler::lor(Register Rd, Register Rs, const Operand& imm, |
+ Condition cond, Register rtmp) { |
+ ASSERT(cond == ne || cond == eq); |
+ 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 |
+ lor(Rd, Rs, imm, rtmp); |
+ bind(&end); |
+} |
+ |
+ |
+void Assembler::lxor(Register Rd, Register Rs, const Operand& imm, |
+ Register rtmp) { |
+ if (Rd.is(r0) && Rd.is(Rs) && FITS_SH4_xor_imm_R0(imm.imm32_)) { |
+ xor_imm_R0_(imm.imm32_); |
+ } else { |
+ ASSERT(!Rs.is(rtmp)); |
+ mov(rtmp, imm); |
+ lxor(Rd, Rs, rtmp); |
+ } |
+} |
+ |
+void Assembler::lxor(Register Rd, Register Rs, Register Rt) { |
+ if (Rs.code() == Rd.code()) |
+ xor_(Rt, Rd); |
+ else if (Rt.code() == Rd.code()) { |
+ xor_(Rs, Rd); |
+ } else { |
+ ASSERT(!Rt.is(Rd) && !Rs.is(Rd)); |
+ mov_(Rs, Rd); |
+ xor_(Rt, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::tst(Register Rd, const Operand& imm, Register rtmp) { |
+ ASSERT(!Rd.is(rtmp)); |
+ mov(rtmp, imm); |
+ tst_(Rd, rtmp); |
+} |
+ |
+void Assembler::call(Label* L) { |
+ jsr(L); |
+} |
+ |
+ |
+void Assembler::db(uint8_t data) { |
+ // No relocation info should be pending while using db. db is used |
+ // to write pure data with no pointers and the constant pool should |
+ // be emitted before using db. |
+ // TODO(STM): constant pool |
+ // ASSERT(num_pending_reloc_info_ == 0); |
+ CheckBuffer(); |
+ *reinterpret_cast<uint8_t*>(pc_) = data; |
+ pc_ += sizeof(uint8_t); |
+} |
+ |
+ |
+void Assembler::dw(uint16_t data) { |
+ // No relocation info should be pending while using db. db is used |
+ // to write pure data with no pointers and the constant pool should |
+ // be emitted before using db. |
+ // TODO(STM): constant pool |
+ // ASSERT(num_pending_reloc_info_ == 0); |
+ CheckBuffer(); |
+ *reinterpret_cast<uint16_t*>(pc_) = data; |
+ pc_ += sizeof(uint16_t); |
+} |
+ |
+ |
+void Assembler::dd(uint32_t data) { |
+ // No relocation info should be pending while using db. db is used |
+ // to write pure data with no pointers and the constant pool should |
+ // be emitted before using db. |
+ // TODO(STM): constant pool |
+ // ASSERT(num_pending_reloc_info_ == 0); |
+ CheckBuffer(); |
+ *reinterpret_cast<uint32_t*>(pc_) = data; |
+ pc_ += sizeof(uint32_t); |
+} |
+ |
+ |
+Register Assembler::GetRn(Instr instr) { |
+ ASSERT(IsCmpRegister(instr) || IsMovImmediate(instr)); |
+ Register reg; |
+ // extract Rn from cmp/xx Rm, Rn |
+ reg.code_ = (instr & 0x0F00) >> 8; |
+ return reg; |
+} |
+ |
+ |
+Register Assembler::GetRm(Instr instr) { |
+ ASSERT(IsCmpRegister(instr) || IsMovImmediate(instr)); |
+ Register reg; |
+ // extract Rn from cmp/xx Rm, Rn |
+ reg.code_ = (instr & 0x00F0) >> 4; |
+ return reg; |
+} |
+ |
+ |
+bool Assembler::IsMovImmediate(Instr instr) { |
+ // mov #ii, Rn |
+ return (instr & 0xF000) == 0xE000; |
+} |
+ |
+ |
+bool Assembler::IsBranch(Instr instr) { |
+ // bt|bf|bt/s|bf/s instrs. |
+ return (instr & 0xF900) == 0x8900; |
+} |
+ |
+ |
+Condition Assembler::GetCondition(Instr instr) { |
+ ASSERT(IsBranch(instr)); |
+ return (instr & 0x200) == 0x200 ? |
+ ne : // bf| bf/s |
+ eq; // bt|bt/s |
+} |
+ |
+ |
+bool Assembler::IsCmpRegister(Instr instr) { |
+ // cmp/eq Rm, Rn |
+ return (instr & 0xF00F) == 0x3000; |
+} |
+ |
+ |
+bool Assembler::IsCmpImmediate(Instr instr) { |
+ // cmp/eq #ii, R0 |
+ return (instr & 0xFF00) == 0x8800; |
+} |
+ |
+ |
+Register Assembler::GetCmpImmediateRegister(Instr instr) { |
+ ASSERT(IsCmpImmediate(instr)); |
+ // The instruction is cmpeq #ii, r0, return r0 |
+ return r0; |
+} |
+ |
+ |
+int Assembler::GetCmpImmediateAsUnsigned(Instr instr) { |
+ ASSERT(IsCmpImmediate(instr)); |
+ // The instruction is cmpeq #ii, r0, return 8-bit #ii as unsigned |
+ return (instr & 0xFF); |
+} |
+ |
+ |
+void Assembler::bind(Label* L) { |
+ // label can only be bound once |
+ ASSERT(!L->is_bound()); |
+ |
+ // Jump directly to the current PC |
+ int target_pos = reinterpret_cast<int>(pc_); |
+ int is_near_linked = L->is_near_linked(); |
+ Label::Distance distance = is_near_linked ? Label::kNear : Label::kFar; |
+ |
+ // List the links to patch |
+ while (L->is_linked()) { |
+ // Compute the current position |
+ // L->pos() is the offset from the begining of the buffer |
+ uint16_t* p_pos = reinterpret_cast<uint16_t*>(L->pos() + buffer_); |
+ ASSERT((is_near_linked && (int)(*p_pos) % 4 != 0) || !is_near_linked); |
+ |
+ // Compute the next before the patch |
+ next(L, distance); |
+ |
+ // Patching |
+ // Is it a backtrack label or a classical one ? |
+ // In this case the LSB is set to 1 |
+ if (!is_near_linked && (((signed)*p_pos) & 0x3) == 1) { |
+ ASSERT(((*reinterpret_cast<int16_t*>(p_pos)) & ~0x3) == kEndOfChain); |
+ *reinterpret_cast<uint32_t*>(p_pos) = target_pos - (unsigned)buffer_ + |
+ (Code::kHeaderSize - kHeapObjectTag); |
+ } else { |
+ patchBranchOffset(target_pos, p_pos, is_near_linked); |
+ } |
+ } |
+ L->bind_to(pc_offset()); |
+ L->UnuseNear(); |
+ |
+ // Keep track of the last bound label so we don't eliminate any instructions |
+ // before a bound label. |
+ if (pc_offset() > last_bound_pos_) |
+ last_bound_pos_ = pc_offset(); |
+} |
+ |
+ |
+void Assembler::next(Label* L, Label::Distance distance) { |
+ if (distance == Label::kNear) { |
+ ASSERT(L->is_near_linked()); |
+ int16_t link = (*reinterpret_cast<uint16_t*>(L->pos() + buffer_)) & ~0x3; |
+ if (link > 0) { |
+ L->link_to(link & ~0x3); |
+ } else { |
+ ASSERT(link == kEndOfChain); |
+ L->Unuse(); |
+ } |
+ } else { |
+ ASSERT(L->is_linked()); |
+ int link = *reinterpret_cast<uint32_t*>(L->pos() + buffer_); |
+ if (link > 0) { |
+ L->link_to(link); |
+ } else { |
+ ASSERT((link & ~0x3) == kEndOfChain); |
+ L->Unuse(); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::load_label(Label* L) { |
+ ASSERT(!L->is_bound()); |
+ int offset = L->is_linked() ? L->pos() : kEndOfChain; |
+ ASSERT((offset % 4) == 0); |
+ mov(r0, Operand(offset + 0x1), true); |
+ L->link_to(pc_offset() - sizeof(uint32_t)); |
+} |
+ |
+ |
+void Assembler::branch(Label* L, Register rtmp, branch_type type, |
+ Label::Distance distance) { |
+ // when bound both near and far labels are represented the same way |
+ if (L->is_bound()) { |
+ ASSERT(L->pos() != kEndOfChain); |
+ branch(L->pos() - pc_offset(), rtmp, type, distance, false); |
+ } else { |
+ // The only difference between Near and far label is in the |
+ // is_near_linked function. |
+ // For this reason, we set the near_link_pos to 1 and then we use the |
+ // generic link_to to know the position |
+ // Moreover the position of the linked branch will be altered when dumped |
+ // on memory to carry the type of branch to write when patching back |
+ if (distance == Label::kNear) |
+ L->link_to(1, Label::kNear); |
+ |
+ if (L->is_linked()) { |
+ ASSERT(L->pos() != kEndOfChain); |
+ branch(L->pos(), rtmp, type, distance, true); |
+ } else { |
+ branch(kEndOfChain, rtmp, type, distance, true); // Patched later on |
+ } |
+ |
+ int pos; |
+ if (distance == Label::kFar) { |
+ // Compensate the place of the constant (sizeof(uint32_t)) |
+ // Constant pool is always emited last in the sequence |
+ // the position is defined as an offset from the begining of the buffer |
+ pos = pc_offset() - sizeof(uint32_t); |
+ } else { |
+ pos = pc_offset() - sizeof(uint16_t); |
+ } |
+ L->link_to(pos); // Link to the constant |
+ } |
+} |
+ |
+ |
+void Assembler::jmp(Register Rd) { |
+ // Record position of a jmp to code |
+ positions_recorder()->WriteRecordedPositions(); |
+ jmp_indRd_(Rd); |
+ nop_(); |
+} |
+ |
+void Assembler::jsr(Register Rd) { |
+ // Record position of a jmp to code |
+ positions_recorder()->WriteRecordedPositions(); |
+ jsr_indRd_(Rd); |
+ nop_(); |
+} |
+ |
+void Assembler::jmp(Handle<Code> code, RelocInfo::Mode rmode, Register rtmp) { |
+ ASSERT(RelocInfo::IsCodeTarget(rmode)); |
+ // Record position of a jmp to code |
+ positions_recorder()->WriteRecordedPositions(); |
+ // TODO(stm): make a faster sequence where the constant pool is |
+ // after the branch |
+ mov(rtmp, Operand(reinterpret_cast<intptr_t>(code.location()), rmode)); |
+ jmp_indRd_(rtmp); |
+ nop_(); |
+} |
+ |
+void Assembler::jsr(Handle<Code> code, RelocInfo::Mode rmode, Register rtmp) { |
+ ASSERT(RelocInfo::IsCodeTarget(rmode)); |
+ // Record position of a jsr to code |
+ positions_recorder()->WriteRecordedPositions(); |
+ mov(rtmp, Operand(reinterpret_cast<intptr_t>(code.location()), rmode)); |
+ jsr_indRd_(rtmp); |
+ nop_(); |
+} |
+ |
+void Assembler::branch(int offset, Register rtmp, branch_type type, |
+ Label::Distance distance, bool patched_later) { |
+ switch (type) { |
+ case branch_true: |
+ conditional_branch(offset, rtmp, distance, patched_later, true); |
+ break; |
+ case branch_false: |
+ conditional_branch(offset, rtmp, distance, patched_later, false); |
+ break; |
+ case branch_unconditional: |
+ jmp(offset, rtmp, distance, patched_later); |
+ break; |
+ case branch_subroutine: |
+ jsr(offset, rtmp, patched_later); |
+ break; |
+ } |
+} |
+ |
+ |
+void Assembler::patchBranchOffset(int target_pos, uint16_t *p_constant, |
+ int is_near_linked) { |
+ if (is_near_linked) { |
+ // The two least significant bits represent the type of branch (1, 2 or 3) |
+ ASSERT((*p_constant & 0x3) == 1 || // bt |
+ (*p_constant & 0x3) == 2 || // bf |
+ (*p_constant & 0x3) == 3); // jmp |
+ int disp = target_pos - (unsigned)p_constant - 4; |
+ |
+ // select the right branch type |
+ switch ((*p_constant & 0x3)) { |
+ case 1: |
+ ASSERT(FITS_SH4_bt(disp)); |
+ *p_constant = (0x8 << 12) | (0x9 << 8) | (((disp & 0x1FE) >> 1) << 0); |
+ break; |
+ case 2: |
+ ASSERT(FITS_SH4_bf(disp)); |
+ *p_constant = (0x8 << 12) | (0xB << 8) | (((disp & 0x1FE) >> 1) << 0); |
+ break; |
+ case 3: |
+ ASSERT(FITS_SH4_bra(disp + 2)); |
+ ASSERT(*(p_constant - 1) == 0x9); |
+ *(p_constant - 1) = (0xA << 12) | ((((disp + 2) & 0x1FFE) >> 1) << 0); |
+ *(p_constant) = 0x9; |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ } |
+ |
+ } else { |
+ // Patch the constant |
+ ASSERT(*(p_constant - 1) == 0x09); |
+ // Is it a jsr or any other branch ? |
+ if (*(p_constant - 2) == 0xa002) |
+ *reinterpret_cast<uint32_t*>(p_constant) = target_pos - |
+ (unsigned)p_constant + 4; |
+ else |
+ *reinterpret_cast<uint32_t*>(p_constant) = target_pos - |
+ (unsigned)p_constant; |
+ } |
+} |
+ |
+ |
+void Assembler::conditional_branch(int offset, Register rtmp, |
+ Label::Distance distance, bool patched_later, |
+ bool type) { |
+ if (patched_later) { |
+ if (distance == Label::kNear) { |
+ align(); |
+ // Use the 2 least significant bits to store the type of branch |
+ // We assume (and assert) that they always are null |
+ ASSERT((offset % 4) == 0); |
+ dw(offset + (type ? 0x1 : 0x2)); |
+ } else { |
+ align(); |
+ type ? bf_(12) : bt_(12); |
+ nop_(); |
+ movl_dispPC_(4, rtmp); |
+ nop_(); |
+ braf_(rtmp); |
+ nop_(); |
+ dd(offset); |
+ } |
+ } else { |
+ if (FITS_SH4_bt(offset - 4)) { |
+ type ? bt_(offset - 4) : bf_(offset - 4); |
+ nop_(); |
+ } else { |
+ int nop_count = align(); |
+ type ? bf_(12) : bt_(12); |
+ nop_(); |
+ movl_dispPC_(4, rtmp); |
+ nop_(); |
+ braf_(rtmp); |
+ nop_(); |
+ dd(offset - 4 - 8 - 2 * nop_count); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::jmp(int offset, Register rtmp, Label::Distance distance, |
+ bool patched_later) { |
+ positions_recorder()->WriteRecordedPositions(); |
+ |
+ // Is it going to be pacthed later on |
+ if (patched_later) { |
+ if (distance == Label::kNear) { |
+ misalign(); |
+ nop(); |
+ ASSERT((offset % 4) == 0); |
+ dw(offset + 0x3); |
+ } else { |
+ // There is no way to know the size of the offset: take the worst case |
+ align(); |
+ movl_dispPC_(4, rtmp); |
+ nop(); |
+ braf_(rtmp); |
+ nop_(); |
+ dd(offset); |
+ } |
+ } else { |
+ // Does it fits in a bra offset |
+ if (FITS_SH4_bra(offset - 4)) { |
+ bra_(offset - 4); |
+ nop_(); |
+ } else { |
+ int nop_count = align(); |
+ movl_dispPC_(4, rtmp); |
+ nop(); |
+ braf_(rtmp); |
+ nop_(); |
+ dd(offset - 4 - 4 - 2 * nop_count); |
+ } |
+ } |
+} |
+ |
+void Assembler::jsr(int offset, Register rtmp, bool patched_later) { |
+ positions_recorder()->WriteRecordedPositions(); |
+ |
+ // Is it going to be patched later on ? |
+ if (patched_later) { |
+ // There is no way to know the size of the offset: take the worst case |
+ align(); |
+ movl_dispPC_(8, rtmp); |
+ nop(); |
+ bsrf_(rtmp); |
+ nop_(); |
+ bra_(4); |
+ nop_(); |
+ dd(offset); |
+ } else { |
+ // Does it fits in a bsr offset |
+ if (FITS_SH4_bsr(offset - 4)) { |
+ bsr_(offset - 4); |
+ nop_(); |
+ } else { |
+ int nop_count = align(); |
+ movl_dispPC_(8, rtmp); |
+ nop(); |
+ bsrf_(rtmp); |
+ nop_(); |
+ bra_(4); |
+ nop_(); |
+ dd(offset - 4 - 4 - 2 * nop_count); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::mov(Register Rd, const Operand& imm, bool force) { |
+ // FIXME(STM): Internal ref not handled |
+ ASSERT(imm.rmode_ != RelocInfo::INTERNAL_REFERENCE); |
+ |
+ // Move based on immediates can only be 8 bits long |
+ if (force == false && (imm.is_int8() && imm.rmode_ == RelocInfo::NONE)) { |
+ mov_imm_(imm.imm32_, Rd); |
+ } else { |
+ // Use a tiny constant pool and jump above |
+ align(); |
+#ifdef DEBUG |
+ int instr_address = pc_offset(); |
+#endif |
+ // Record the relocation location (after the align). |
+ // Actually we record the PC of the instruction, |
+ // though the target address is encoded in the constant pool below. |
+ // If the code sequence changes, one must update |
+ // Assembler::target_address_address_at(). |
+ if (imm.rmode_ != RelocInfo::NONE) RecordRelocInfo(imm.rmode_); |
+ movl_dispPC_(4, Rd); |
+ nop_(); |
+ bra_(4); |
+ nop_(); |
+#ifdef DEBUG |
+ if (imm.rmode_ != RelocInfo::NONE) { |
+ Address target_address = pc_; |
+ // Verify that target_address_address_at() is actually returning |
+ // the address where the target address for the instruction is stored. |
+ ASSERT(target_address == |
+ target_pointer_address_at( |
+ reinterpret_cast<byte*>(buffer_ + instr_address))); |
+ } |
+#endif |
+ dd(imm.imm32_); |
+ } |
+} |
+ |
+ |
+void Assembler::addpc(Register Rd, int offset, Register Pr) { |
+ // We compute a pc+offset value where the pc |
+ // is the pc after this code sequence. |
+ // In order to do this, we do a bsr and get the link register. |
+ ASSERT(Pr.is(pr)); |
+ bsr_(0); |
+ nop_(); |
+ sts_PR_(Rd); |
+ add_imm_(4+offset, Rd); |
+} |
+ |
+ |
+void Assembler::mov(Register Rd, Register Rs, Condition cond) { |
+ ASSERT(cond == ne || cond == eq || cond == al); |
+ // If cond is eq, we move Rs into Rd, otherwise, nop |
+ if (cond == eq) |
+ bf_(0); // Jump after sequence if T bit is false |
+ else if (cond == ne) |
+ bt_(0); // Jump after sequence if T bit is true |
+ if (Rs.is(pr)) { |
+ ASSERT(Rd.is_valid()); |
+ sts_PR_(Rd); |
+ } else if (Rd.is(pr)) { |
+ ASSERT(Rs.is_valid()); |
+ lds_PR_(Rs); |
+ } else { |
+ ASSERT(Rs.is_valid()); |
+ ASSERT(Rd.is_valid()); |
+ mov_(Rs, Rd); |
+ } |
+} |
+ |
+ |
+void Assembler::mov(Register Rd, const Operand& imm, Condition cond) { |
+ ASSERT(cond == ne || cond == eq); |
+ if (FITS_SH4_mov_imm(imm.imm32_)) { |
+ // If cond is eq, we move Rs into Rd, otherwise, nop |
+ if (cond == eq) |
+ bf_(0); // Jump after sequence if T bit is false |
+ else |
+ bt_(0); // Jump after sequence if T bit is true |
+ mov_imm_(imm.imm32_, Rd); |
+ } else { |
+ Label skip; |
+ if (cond == eq) |
+ bf_near(&skip); |
+ else |
+ bt_near(&skip); |
+ mov(Rd, imm); |
+ bind(&skip); |
+ } |
+} |
+ |
+ |
+void Assembler::mov(Register Rd, const MemOperand& src, Register rtmp) { |
+ ASSERT(src.mode_ == Offset); |
+ if (src.rn_.is_valid()) { |
+ ASSERT(rtmp.is_valid()); |
+ add(rtmp, src.rm_, src.rn_); |
+ movl_indRs_(rtmp, Rd); |
+ } else { |
+ if (src.offset_ == 0) { |
+ movl_indRs_(src.rm_, Rd); |
+ } else if (FITS_SH4_movl_dispRs(src.offset_)) { |
+ movl_dispRs_(src.offset_, src.rm_, Rd); |
+ } else { |
+ ASSERT(rtmp.is_valid()); |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ movl_indRs_(rtmp, Rd); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::movb(Register Rd, const MemOperand& src, Register rtmp) { |
+ ASSERT(src.mode_ == Offset); |
+ if (src.rn_.is_valid()) { |
+ add(rtmp, src.rm_, src.rn_); |
+ movb_indRs_(rtmp, Rd); |
+ } else { |
+ if (src.offset_ == 0) { |
+ movb_indRs_(src.rm_, Rd); |
+ } else { |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ movb_indRs_(rtmp, Rd); |
+ } |
+ } |
+ extub_(Rd, Rd); // zero extension |
+} |
+ |
+ |
+void Assembler::movw(Register Rd, const MemOperand& src, Register rtmp) { |
+ ASSERT(src.mode_ == Offset); |
+ if (src.rn_.is_valid()) { |
+ add(rtmp, src.rm_, src.rn_); |
+ movw_indRs_(rtmp, Rd); |
+ } else { |
+ if (src.offset_ == 0) { |
+ movw_indRs_(src.rm_, Rd); |
+ } else { |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ movw_indRs_(rtmp, Rd); |
+ } |
+ } |
+ // Zero extension |
+ extuw_(Rd, Rd); |
+} |
+ |
+ |
+void Assembler::movd(DwVfpRegister Dd, Register Rs1, Register Rs2) { |
+ align(); |
+ push(Rs1); |
+ push(Rs2); |
+ fmov_incRs_(sp, Dd.low()); |
+ fmov_incRs_(sp, Dd.high()); |
+} |
+ |
+ |
+void Assembler::movd(Register Rd1, Register Rd2, DwVfpRegister Ds) { |
+ align(); |
+ fmov_decRd_(Ds.low(), sp); |
+ fmov_decRd_(Ds.high(), sp); |
+ pop(Rd1); |
+ pop(Rd2); |
+} |
+ |
+ |
+void Assembler::ldrsb(Register Rd, const MemOperand& src, Register rtmp) { |
+ ASSERT(src.mode_ == Offset); |
+ if (src.rn_.is_valid()) { |
+ add(rtmp, src.rm_, src.rn_); |
+ movb_indRs_(rtmp, Rd); |
+ } else { |
+ if (src.offset_ == 0) { |
+ movb_indRs_(src.rm_, Rd); |
+ } else { |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ movb_indRs_(rtmp, Rd); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::ldrsh(Register Rd, const MemOperand& src, Register rtmp) { |
+ ASSERT(src.mode_ == Offset); |
+ if (src.rn_.is_valid()) { |
+ add(rtmp, src.rm_, src.rn_); |
+ movw_indRs_(rtmp, Rd); |
+ } else { |
+ if (src.offset_ == 0) { |
+ movw_indRs_(src.rm_, Rd); |
+ } else { |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ movw_indRs_(rtmp, Rd); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::fldr(SwVfpRegister dst, const MemOperand& src, Register rtmp) { |
+ if (src.rn_.is_valid()) { |
+ add(rtmp, src.rm_, src.rn_); |
+ fmov_indRs_(rtmp, dst); |
+ } else { |
+ if (src.offset_ == 0) { |
+ fmov_indRs_(src.rm_, dst); |
+ } else { |
+ ASSERT(src.rn_.is(no_reg)); |
+ add(rtmp, src.rm_, Operand(src.offset_)); |
+ fmov_indRs_(rtmp, dst); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::dldr(DwVfpRegister dst, const MemOperand& src, Register rtmp) { |
+ if (src.rn_.is_valid()) { |
+ UNIMPLEMENTED(); |
+ } else { |
+ fldr(dst.high(), src, rtmp); |
+ fldr(dst.low(), MemOperand(src.rm_, src.offset_ + 4), rtmp); |
+ } |
+} |
+ |
+ |
+void Assembler::fstr(SwVfpRegister src, const MemOperand& dst, Register rtmp) { |
+ if (dst.rn_.is_valid()) { |
+ add(rtmp, dst.rm_, dst.rn_); |
+ fmov_indRd_(src, rtmp); |
+ } else { |
+ if (dst.offset_ == 0) { |
+ fmov_indRd_(src, dst.rm_); |
+ } else { |
+ ASSERT(dst.rn_.is(no_reg)); |
+ add(rtmp, dst.rm_, Operand(dst.offset_)); |
+ fmov_indRd_(src, rtmp); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::dstr(DwVfpRegister src, const MemOperand& dst, Register rtmp) { |
+ if (dst.rn_.is_valid()) { |
+ UNIMPLEMENTED(); |
+ } else { |
+ fstr(src.high(), dst, rtmp); |
+ fstr(src.low(), MemOperand(dst.rm_, dst.offset_ + 4), rtmp); |
+ } |
+} |
+ |
+ |
+void Assembler::dfloat(DwVfpRegister Dd, const Operand &imm, Register rtmp) { |
+ mov(rtmp, imm); |
+ dfloat(Dd, rtmp); |
+} |
+ |
+ |
+void Assembler::dfloat(DwVfpRegister Dd, Register Rs) { |
+ lds_FPUL_(Rs); |
+ float_FPUL_double_(Dd); |
+} |
+ |
+ |
+void Assembler::dufloat(DwVfpRegister Dd, Register Rs, DwVfpRegister drtmp, |
+ Register rtmp) { |
+ Label too_large, end; |
+ |
+ // Test the sign bit to see if the conversion from unsigned to signed is safe |
+ tst(Rs, Operand(0x80000000), rtmp); |
+ bf(&too_large); |
+ |
+ // The unsigned integer is smal enough to be a signed one |
+ lds_FPUL_(Rs); |
+ float_FPUL_double_(Dd); |
+ b(&end); |
+ |
+ // Do some correction to convert the unsigned to a floating point value |
+ bind(&too_large); |
+ dfloat(drtmp, Operand(0x7fffffff), rtmp); |
+ dfloat(Dd, Operand(1), rtmp); |
+ fadd(drtmp, Dd); |
+ fadd(drtmp, drtmp); |
+ dfloat(Dd, Rs); |
+ fadd(Dd, drtmp); |
+ |
+ bind(&end); |
+} |
+ |
+ |
+void Assembler::idouble(Register Rd, DwVfpRegister Ds, Register fpscr) { |
+ ftrc_double_FPUL_(Ds); |
+ if (!fpscr.is(no_reg)) |
+ sts_FPSCR_(fpscr); |
+ sts_FPUL_(Rd); |
+} |
+ |
+ |
+void Assembler::isingle(Register Rd, SwVfpRegister Fs) { |
+ flds_FPUL_(Fs); |
+ sts_FPUL_(Rd); |
+} |
+ |
+ |
+void Assembler::mov(const MemOperand& dst, Register Rd, Register rtmp) { |
+ ASSERT(dst.mode_ == Offset); |
+ if (dst.rn_.is_valid()) { |
+ add(rtmp, dst.rm_, dst.rn_); |
+ movl_indRd_(Rd, rtmp); |
+ } else { |
+ if (dst.offset_ == 0) { |
+ movl_indRd_(Rd, dst.rm_); |
+ } else { |
+ if (FITS_SH4_movl_dispRd(dst.offset_)) { |
+ movl_dispRd_(Rd, dst.offset_, dst.rm_); |
+ } else { |
+ ASSERT(!Rd.is(rtmp)); |
+ add(rtmp, dst.rm_, Operand(dst.offset_)); |
+ movl_indRd_(Rd, rtmp); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::movb(const MemOperand& dst, Register Rd, Register rtmp) { |
+ ASSERT(dst.mode_ == Offset); |
+ if (dst.rn_.is_valid()) { |
+ add(rtmp, dst.rm_, dst.rn_); |
+ movb_indRd_(Rd, rtmp); |
+ } else { |
+ if (dst.offset_ == 0) { |
+ movb_indRd_(Rd, dst.rm_); |
+ } else { |
+ ASSERT(!Rd.is(rtmp)); |
+ add(rtmp, dst.rm_, Operand(dst.offset_)); |
+ movb_indRd_(Rd, rtmp); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::movw(const MemOperand& dst, Register Rd, Register rtmp) { |
+ ASSERT(dst.mode_ == Offset); |
+ if (dst.rn_.is_valid()) { |
+ add(rtmp, dst.rm_, dst.rn_); |
+ movw_indRd_(Rd, rtmp); |
+ } else { |
+ if (dst.offset_ == 0) { |
+ movw_indRd_(Rd, dst.rm_); |
+ } else { |
+ ASSERT(!Rd.is(rtmp)); |
+ add(rtmp, dst.rm_, Operand(dst.offset_)); |
+ movw_indRd_(Rd, rtmp); |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::mul(Register Rd, Register Rs, Register Rt) { |
+ mull_(Rs, Rt); |
+ sts_MACL_(Rd); |
+} |
+ |
+ |
+void Assembler::dmuls(Register dstL, Register dstH, Register src1, |
+ Register src2) { |
+ dmulsl_(src1, src2); |
+ sts_MACL_(dstL); |
+ sts_MACH_(dstH); |
+} |
+ |
+ |
+void Assembler::pop(Register dst) { |
+ if (dst.is(pr)) |
+ ldsl_incRd_PR_(sp); |
+ else |
+ movl_incRs_(sp, dst); |
+} |
+ |
+ |
+void Assembler::pop(DwVfpRegister dst) { |
+ fmov_incRs_(sp, SwVfpRegister::from_code(dst.code())); |
+ fmov_incRs_(sp, SwVfpRegister::from_code(dst.code()+1)); |
+} |
+ |
+ |
+void Assembler::push(Register src) { |
+ if (src.is(pr)) |
+ stsl_PR_decRd_(sp); |
+ else |
+ movl_decRd_(src, sp); |
+} |
+ |
+ |
+void Assembler::push(DwVfpRegister src) { |
+ fmov_decRd_(SwVfpRegister::from_code(src.code()), sp); |
+ fmov_decRd_(SwVfpRegister::from_code(src.code()+1), sp); |
+} |
+ |
+ |
+void Assembler::push(const Operand& op, Register rtmp) { |
+ mov(rtmp, op); |
+ push(rtmp); |
+} |
+ |
+ |
+void Assembler::pushm(RegList dst, bool doubles) { |
+ if (!doubles) { |
+ for (int16_t i = Register::kNumRegisters - 1; i >= 0; i--) { |
+ if ((dst & (1 << i)) != 0) { |
+ push(Register::from_code(i)); |
+ } |
+ } |
+ } else { |
+ for (int16_t i = DwVfpRegister::kNumRegisters - 1; i >= 0; i -= 2) { |
+ if ((dst & (1 << i)) != 0) { |
+ push(DwVfpRegister::from_code(i)); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void Assembler::popm(RegList src, bool doubles) { |
+ if (!doubles) { |
+ for (uint16_t i = 0; i < Register::kNumRegisters; i++) { |
+ if ((src & (1 << i)) != 0) { |
+ pop(Register::from_code(i)); |
+ } |
+ } |
+ } else { |
+ for (uint16_t i = 0; i < Register::kNumRegisters; i += 2) { |
+ if ((src & (1 << i)) != 0) { |
+ pop(DwVfpRegister::from_code(i)); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+// Exception-generating instructions and debugging support. |
+// Stops with a non-negative code less than kNumOfWatchedStops support |
+// enabling/disabling and a counter feature. See simulator-arm.h . |
+void Assembler::stop(const char* msg) { |
+ // TODO(stm): handle simulator based stuff (ref to ARM code) |
+ bkpt(); |
+} |
+ |
+ |
+void Assembler::bkpt() { |
+ // Use a privileged instruction. |
+ // Will generate an illegal instruction exception code: 0x180. |
+ ldtlb_(); |
+} |
+ |
+ |
+#ifdef SH4_DUMP_BUFFER |
+static int buffer_count = 0; |
+#endif |
+ |
+Assembler::~Assembler() { |
+#ifdef SH4_DUMP_BUFFER |
+ // dump the buffer on the disk |
+ printf("dumping a buffer %i\n", buffer_count++); |
+ char *psz_filename; |
+ asprintf(&psz_filename, "buffer-%d.st40", buffer_count); |
+ FILE *dump = fopen(psz_filename, "w"); |
+ if (dump) { |
+ fwrite(buffer_, buffer_size_, 1, dump); |
+ fclose(dump); |
+ } |
+ free(psz_filename); |
+#endif |
+ |
+ if (own_buffer_) { |
+ if (isolate()->assembler_spare_buffer() == NULL && |
+ buffer_size_ == kMinimalBufferSize) { |
+ isolate()->set_assembler_spare_buffer(buffer_); |
+ } else { |
+ DeleteArray(buffer_); |
+ } |
+ } |
+} |
+ |
+ |
+const int RelocInfo::kApplyMask = 0; |
+ |
+bool RelocInfo::IsCodedSpecially() { |
+ UNIMPLEMENTED(); |
+ return false; |
+} |
+ |
+ |
+} } // namespace v8::internal |
+ |
+#endif // V8_TARGET_ARCH_SH4 |