Add miscellaneous operations to x64 assembler.
authorwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Jun 2009 13:40:52 +0000 (13:40 +0000)
committerwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 2 Jun 2009 13:40:52 +0000 (13:40 +0000)
Review URL: http://codereview.chromium.org/113997

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2089 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/memory.h
src/x64/assembler-x64-inl.h
src/x64/assembler-x64.cc
src/x64/assembler-x64.h

index 1e36bf54ce60dea276908048c632b6f76f8048c2..c64699ee3b95ce38df04714cea3c551465cc823f 100644 (file)
@@ -36,6 +36,10 @@ namespace internal {
 
 class Memory {
  public:
+  static uint16_t& uint16_at(Address addr)  {
+    return *reinterpret_cast<uint16_t*>(addr);
+  }
+
   static uint32_t& uint32_at(Address addr)  {
     return *reinterpret_cast<uint32_t*>(addr);
   }
index ad349345a413d2dfd3a14a886b7bcc2645f2eaf8..8d82a53ff13b204a73a2f78c5c40d294992de889 100644 (file)
@@ -55,6 +55,12 @@ void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) {
 }
 
 
+void Assembler::emitw(uint16_t x) {
+  Memory::uint16_at(pc_) = x;
+  pc_ += sizeof(uint16_t);
+}
+
+
 // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
 // REX.W is set.  REX.X is cleared.
 void Assembler::emit_rex_64(Register reg, Register rm_reg) {
@@ -118,14 +124,14 @@ void Assembler::emit_optional_rex_32(Register reg, const Operand& op) {
 }
 
 
-void Assembler::set_target_address_at(byte* location, byte* value) {
-  UNIMPLEMENTED();
+Address Assembler::target_address_at(Address pc) {
+  return Memory::Address_at(pc);
 }
 
 
-byte* Assembler::target_address_at(byte* location) {
-  UNIMPLEMENTED();
-  return NULL;
+void Assembler::set_target_address_at(Address pc, Address target) {
+  Memory::Address_at(pc) = target;
+  CPU::FlushICache(pc, sizeof(intptr_t));
 }
 
 
@@ -166,6 +172,8 @@ void RelocInfo::set_target_address(Address target) {
   ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
   Assembler::set_target_address_at(pc_, target);
 }
+
+
 Object* RelocInfo::target_object() {
   ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
   return *reinterpret_cast<Object**>(pc_);
index 589db08898cd586c72b4b0681766a224a8afd51a..ed9f2ad62e6537a999eb3ba0a7b84eedb73d4b0b 100644 (file)
@@ -77,7 +77,73 @@ uint64_t CpuFeatures::supported_ = 0;
 uint64_t CpuFeatures::enabled_ = 0;
 
 void CpuFeatures::Probe()  {
-  // TODO(X64): UNIMPLEMENTED
+  ASSERT(Heap::HasBeenSetup());
+  ASSERT(supported_ == 0);
+  if (Serializer::enabled()) return;  // No features if we might serialize.
+
+  Assembler assm(NULL, 0);
+  Label cpuid, done;
+#define __ assm.
+  // Save old esp, since we are going to modify the stack.
+  __ push(rbp);
+  __ pushfq();
+  __ push(rcx);
+  __ push(rbx);
+  __ movq(rbp, rsp);
+
+  // If we can modify bit 21 of the EFLAGS register, then CPUID is supported.
+  __ pushfq();
+  __ pop(rax);
+  __ movq(rdx, rax);
+  __ xor_(rax, Immediate(0x200000));  // Flip bit 21.
+  __ push(rax);
+  __ popfq();
+  __ pushfq();
+  __ pop(rax);
+  __ xor_(rax, rdx);  // Different if CPUID is supported.
+  __ j(not_zero, &cpuid);
+
+  // CPUID not supported. Clear the supported features in edx:eax.
+  __ xor_(rax, rax);
+  __ jmp(&done);
+
+  // Invoke CPUID with 1 in eax to get feature information in
+  // ecx:edx. Temporarily enable CPUID support because we know it's
+  // safe here.
+  __ bind(&cpuid);
+  __ movq(rax, Immediate(1));
+  supported_ = (1 << CPUID);
+  { Scope fscope(CPUID);
+    __ cpuid();
+  }
+  supported_ = 0;
+
+  // Move the result from ecx:edx to rax and make sure to mark the
+  // CPUID feature as supported.
+  __ movl(rax, rdx);  // Zero-extended to 64 bits.
+  __ shl(rcx, Immediate(32));
+  __ or_(rax, rcx);
+  __ or_(rax, Immediate(1 << CPUID));
+
+  // Done.
+  __ bind(&done);
+  __ movq(rsp, rbp);
+  __ pop(rbx);
+  __ pop(rcx);
+  __ popfq();
+  __ pop(rbp);
+  __ ret(0);
+#undef __
+
+  CodeDesc desc;
+  assm.GetCode(&desc);
+  Object* code =
+      Heap::CreateCode(desc, NULL, Code::ComputeFlags(Code::STUB), NULL);
+  if (!code->IsCode()) return;
+  LOG(CodeCreateEvent("Builtin", Code::cast(code), "CpuFeatures::Probe"));
+  typedef uint64_t (*F0)();
+  F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
+  supported_ = probe();
 }
 
 // -----------------------------------------------------------------------------
@@ -368,6 +434,26 @@ void Assembler::shift(Register dst, int subcode) {
 }
 
 
+void Assembler::bt(const Operand& dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64(src, dst);
+  emit(0x0F);
+  emit(0xA3);
+  emit_operand(src, dst);
+}
+
+
+void Assembler::bts(const Operand& dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64(src, dst);
+  emit(0x0F);
+  emit(0xAB);
+  emit_operand(src, dst);
+}
+
+
 void Assembler::call(Label* L) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -401,6 +487,23 @@ void Assembler::call(Register adr) {
 }
 
 
+void Assembler::cpuid() {
+  ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID));
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit(0x0F);
+  emit(0xA2);
+}
+
+
+void Assembler::cqo() {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64();
+  emit(0x99);
+}
+
+
 void Assembler::dec(Register dst) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -419,6 +522,15 @@ void Assembler::dec(const Operand& dst) {
 }
 
 
+void Assembler::enter(Immediate size) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit(0xC8);
+  emitw(size.value_);  // 16 bit operand, always.
+  emit(0);
+}
+
+
 void Assembler::hlt() {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -530,6 +642,49 @@ void Assembler::jmp(Register target) {
 }
 
 
+void Assembler::lea(Register dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64(dst, src);
+  emit(0x8D);
+  emit_operand(dst, src);
+}
+
+
+void Assembler::leave() {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit(0xC9);
+}
+
+
+void Assembler::movl(Register dst, const Operand& src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_optional_rex_32(dst, src);
+  emit(0x8B);
+  emit_operand(dst, src);
+}
+
+
+void Assembler::movl(Register dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_optional_rex_32(dst, src);
+  emit(0x8B);
+  emit(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
+}
+
+
+void Assembler::movl(const Operand& dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_optional_rex_32(src, dst);
+  emit(0x89);
+  emit_operand(src, dst);
+}
+
+
 void Assembler::movq(Register dst, const Operand& src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -548,6 +703,15 @@ void Assembler::movq(Register dst, Register src) {
 }
 
 
+void Assembler::movq(const Operand& dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64(src, dst);
+  emit(0x89);
+  emit_operand(src, dst);
+}
+
+
 void Assembler::movq(Register dst, Immediate value) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -716,6 +880,13 @@ void Assembler::pop(const Operand& dst) {
 }
 
 
+void Assembler::popfq() {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit(0x9D);
+}
+
+
 void Assembler::push(Register src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -735,6 +906,43 @@ void Assembler::push(const Operand& src) {
 }
 
 
+void Assembler::push(Immediate value) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  if (is_int8(value.value_)) {
+    emit(0x6A);
+    emit(value.value_);  // Emit low byte of value.
+  } else {
+    emit(0x68);
+    emitl(value.value_);
+  }
+}
+
+
+void Assembler::pushfq() {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit(0x9C);
+}
+
+
+void Assembler::rcl(Register dst, uint8_t imm8) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  ASSERT(is_uint6(imm8));  // illegal shift count
+  if (imm8 == 1) {
+    emit_rex_64(dst);
+    emit(0xD1);
+    emit(0xD0 | (dst.code() & 0x7));
+  } else {
+    emit_rex_64(dst);
+    emit(0xC1);
+    emit(0xD0 | (dst.code() & 0x7));
+    emit(imm8);
+  }
+}
+
+
 void Assembler::ret(int imm16) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -749,6 +957,21 @@ void Assembler::ret(int imm16) {
 }
 
 
+void Assembler::xchg(Register dst, Register src) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  if (src.is(rax) || dst.is(rax)) {  // Single-byte encoding
+    Register other = src.is(rax) ? dst : src;
+    emit_rex_64(other);
+    emit(0x90 | (other.code() & 0x7));
+  } else {
+    emit_rex_64(src, dst);
+    emit(0x87);
+    emit(0xC0 | (src.code() & 0x7) << 3 | (dst.code() & 0x7));
+  }
+}
+
+
 void Assembler::testb(Register reg, Immediate mask) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
index b9973ef95316021d1a2943ac7e3ba3b6ad126079..be88162000f2c2aaeba5dc2590c2d298e5cf15fc 100644 (file)
@@ -294,11 +294,11 @@ class CpuFeatures : public AllStatic {
   static void Probe();
   // Check whether a feature is supported by the target CPU.
   static bool IsSupported(Feature f) {
-    return (supported_ & (static_cast<uint64_t>(1) << f)) != 0;
+    return (supported_ & (V8_UINT64_C(1) << f)) != 0;
   }
   // Check whether a feature is currently enabled.
   static bool IsEnabled(Feature f) {
-    return (enabled_ & (static_cast<uint64_t>(1) << f)) != 0;
+    return (enabled_ & (V8_UINT64_C(1) << f)) != 0;
   }
   // Enable a specified feature within a scope.
   class Scope BASE_EMBEDDED {
@@ -307,7 +307,7 @@ class CpuFeatures : public AllStatic {
     explicit Scope(Feature f) {
       ASSERT(CpuFeatures::IsSupported(f));
       old_enabled_ = CpuFeatures::enabled_;
-      CpuFeatures::enabled_ |= (static_cast<uint64_t>(1) << f);
+      CpuFeatures::enabled_ |= (V8_UINT64_C(1) << f);
     }
     ~Scope() { CpuFeatures::enabled_ = old_enabled_; }
    private:
@@ -355,8 +355,9 @@ class Assembler : public Malloced {
   void GetCode(CodeDesc* desc);
 
   // Read/Modify the code target 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);
+  // On the x64 architecture, the address is absolute, not relative.
+  static inline Address target_address_at(Address pc);
+  static inline void set_target_address_at(Address pc, Address target);
 
   // Distance between the address of the code target in the call instruction
   // and the return address
@@ -387,13 +388,10 @@ class Assembler : public Malloced {
   void Align(int m);
 
   // Stack
-  void pushad();
-  void popad();
+  void pushfq();
+  void popfq();
 
-  void pushfd();
-  void popfd();
-
-  void push(const Immediate& x);
+  void push(Immediate value);
   void push(Register src);
   void push(const Operand& src);
   void push(Label* label, RelocInfo::Mode relocation_mode);
@@ -401,7 +399,7 @@ class Assembler : public Malloced {
   void pop(Register dst);
   void pop(const Operand& dst);
 
-  void enter(const Immediate& size);
+  void enter(Immediate size);
   void leave();
 
   // Moves
@@ -409,6 +407,10 @@ class Assembler : public Malloced {
   void movb(const Operand& dst, int8_t imm8);
   void movb(const Operand& dst, Register src);
 
+  void movl(Register dst, Register src);
+  void movl(Register dst, const Operand& src);
+  void movl(const Operand& dst, Register src);
+
   void movq(Register dst, int32_t imm32);
   void movq(Register dst, Immediate x);
   void movq(Register dst, const Operand& src);
@@ -515,7 +517,8 @@ class Assembler : public Malloced {
   void dec(Register dst);
   void dec(const Operand& dst);
 
-  void cdq();
+  // Sign-extends rax into rdx:rax.
+  void cqo();
 
   void idiv(Register src);
 
@@ -830,6 +833,7 @@ class Assembler : public Malloced {
   inline void emitl(uint32_t x);
   inline void emit(Handle<Object> handle);
   inline void emitq(uint64_t x, RelocInfo::Mode rmode);
+  inline void emitw(uint16_t x);
   void emit(Immediate x) { emitl(x.value_); }
 
   // Emits a REX prefix that encodes a 64-bit operand size and
@@ -858,6 +862,9 @@ class Assembler : public Malloced {
   // REX.W is set and REX.R clear.
   inline void emit_rex_64(const Operand& op);
 
+  // Emit a REX prefix that only sets REX.W to choose a 64-bit operand size.
+  void emit_rex_64() { emit(0x48); }
+
   // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
   // REX.W is clear.
   inline void emit_rex_32(Register reg, Register rm_reg);