Add implementation of control flow and label binding to x64 assembler.
authorwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 26 May 2009 12:32:09 +0000 (12:32 +0000)
committerwhesse@chromium.org <whesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 26 May 2009 12:32:09 +0000 (12:32 +0000)
Review URL: http://codereview.chromium.org/113832

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

src/x64/assembler-x64-inl.h
src/x64/assembler-x64.cc
src/x64/assembler-x64.h
test/cctest/test-assembler-x64.cc

index ff37f05aba0fef4b468d4e6586c08a2384de0ac4..418525b537ad3709f239dd2a4bceb7ad8c5ea3bf 100644 (file)
@@ -44,6 +44,10 @@ Condition NegateCondition(Condition cc) {
 #define EMIT(x)                                 \
   *pc_++ = (x)
 
+void Assembler::emit(uint32_t x) {
+  *reinterpret_cast<uint32_t*>(pc_) = x;
+  pc_ += sizeof(uint32_t);
+}
 
 void Assembler::emit_rex_64(Register reg, Register rm_reg) {
   EMIT(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
index a95fe8b8125056c87dd7eaf6aff25f57f17c8e4a..4442b5380bb69867daa706493d4c7ca0a2d624f8 100644 (file)
@@ -189,10 +189,34 @@ void Assembler::RecordStatementPosition(int a) {
   UNIMPLEMENTED();
 }
 
-void Assembler::bind(Label* a) {
-  UNIMPLEMENTED();
+
+void Assembler::bind_to(Label* L, int pos) {
+  ASSERT(!L->is_bound());  // Label may only be bound once.
+  last_pc_ = NULL;
+  ASSERT(0 <= pos && pos <= pc_offset());  // Position must be valid.
+  if (L->is_linked()) {
+    int current = L->pos();
+    int next = long_at(current);
+    while (next != current) {
+      // relative address, relative to point after address
+      int imm32 = pos - (current + sizeof(int32_t));
+      long_at_put(current, imm32);
+      current = next;
+      next = long_at(next);
+    }
+    // Fix up last fixup on linked list.
+    int last_imm32 = pos - (current + sizeof(int32_t));
+    long_at_put(current, last_imm32);
+  }
+  L->bind_to(pos);
 }
 
+
+void Assembler::bind(Label* L) {
+  bind_to(L, pc_offset());
+}
+
+
 void Assembler::GrowBuffer() {
   ASSERT(overflow());  // should not call this otherwise
   if (!own_buffer_) FATAL("external code buffer is too small");
@@ -275,6 +299,8 @@ void Assembler::emit_operand(Register reg, const Operand& adr) {
 }
 
 
+// Assembler Instruction implementations
+
 void Assembler::add(Register dst, const Operand& src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -289,7 +315,28 @@ void Assembler::add(Register dst, Register src) {
   last_pc_ = pc_;
   emit_rex_64(dst, src);
   EMIT(0x03);
-  EMIT(0xC0 | (src.code() & 0x7) << 3 | (dst.code() & 0x7));
+  EMIT(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
+}
+
+
+void Assembler::call(Label* L) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  // 1110 1000 #32-bit disp
+  EMIT(0xE8);
+  if (L->is_bound()) {
+    int offset = L->pos() - pc_offset() - sizeof(int32_t);
+    ASSERT(offset <= 0);
+    emit(offset);
+  } else if (L->is_linked()) {
+    emit(L->pos());
+    L->link_to(pc_offset() - sizeof(int32_t));
+  } else {
+    ASSERT(L->is_unused());
+    int32_t current = pc_offset();
+    emit(current);
+    L->link_to(current);
+  }
 }
 
 
@@ -343,6 +390,73 @@ void Assembler::int3() {
 }
 
 
+void Assembler::j(Condition cc, Label* L) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  ASSERT(0 <= cc && cc < 16);
+  if (L->is_bound()) {
+    const int short_size = 2;
+    const int long_size  = 6;
+    int offs = L->pos() - pc_offset();
+    ASSERT(offs <= 0);
+    if (is_int8(offs - short_size)) {
+      // 0111 tttn #8-bit disp
+      EMIT(0x70 | cc);
+      EMIT((offs - short_size) & 0xFF);
+    } else {
+      // 0000 1111 1000 tttn #32-bit disp
+      EMIT(0x0F);
+      EMIT(0x80 | cc);
+      emit(offs - long_size);
+    }
+  } else if (L->is_linked()) {
+    // 0000 1111 1000 tttn #32-bit disp
+    EMIT(0x0F);
+    EMIT(0x80 | cc);
+    emit(L->pos());
+    L->link_to(pc_offset() - sizeof(int32_t));
+  } else {
+    ASSERT(L->is_unused());
+    EMIT(0x0F);
+    EMIT(0x80 | cc);
+    int32_t current = pc_offset();
+    emit(current);
+    L->link_to(current);
+  }
+}
+
+
+void Assembler::jmp(Label* L) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  if (L->is_bound()) {
+    int offs = L->pos() - pc_offset() - 1;
+    ASSERT(offs <= 0);
+    if (is_int8(offs - sizeof(int8_t))) {
+      // 1110 1011 #8-bit disp
+      EMIT(0xEB);
+      EMIT((offs - sizeof(int8_t)) & 0xFF);
+    } else {
+      // 1110 1001 #32-bit disp
+      EMIT(0xE9);
+      emit(offs - sizeof(int32_t));
+    }
+  } else  if (L->is_linked()) {
+    // 1110 1001 #32-bit disp
+    EMIT(0xE9);
+    emit(L->pos());
+    L->link_to(pc_offset() - sizeof(int32_t));
+  } else {
+    // 1110 1001 #32-bit disp
+    ASSERT(L->is_unused());
+    EMIT(0xE9);
+    int32_t current = pc_offset();
+    emit(current);
+    L->link_to(current);
+  }
+}
+
+
 void Assembler::mov(Register dst, const Operand& src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -356,8 +470,8 @@ void Assembler::mov(Register dst, Register src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
   emit_rex_64(dst, src);
-  EMIT(0x89);
-  EMIT(0xC0 | (src.code() & 0x7) << 3 | (dst.code() & 0x7));
+  EMIT(0x8B);
+  EMIT(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
 }
 
 
index a5db7b44ddf64088523c8f30b094ea70f6104664..e57f5fd770c8a568923820bec093986cf2c1e99c 100644 (file)
@@ -676,9 +676,9 @@ class Assembler : public Malloced {
   void jmp(Handle<Code> code, RelocInfo::Mode rmode);
 
   // Conditional jumps
-  void j(Condition cc, Label* L, Hint hint = no_hint);
-  void j(Condition cc, byte* entry, RelocInfo::Mode rmode, Hint hint = no_hint);
-  void j(Condition cc, Handle<Code> code, Hint hint = no_hint);
+  void j(Condition cc, Label* L);
+  void j(Condition cc, byte* entry, RelocInfo::Mode rmode);
+  void j(Condition cc, Handle<Code> code);
 
   // Floating-point operations
   void fld(int i);
index 4c166983cb1c416f3c2de2d3d07392c4e95de431..1179fff53cb743ed0e4b6923cae183c0326c9a66 100644 (file)
 using v8::internal::byte;
 using v8::internal::OS;
 using v8::internal::Assembler;
+using v8::internal::Operand;
+using v8::internal::Label;
 using v8::internal::rax;
 using v8::internal::rsi;
+using v8::internal::rdi;
+using v8::internal::rbp;
+using v8::internal::rsp;
 using v8::internal::FUNCTION_CAST;
 using v8::internal::CodeDesc;
 
@@ -60,7 +65,7 @@ typedef int (*F2)(int x, int y);
 #define __ assm.
 
 
-TEST(AssemblerX640) {
+TEST(AssemblerX64ReturnOperation) {
   // Allocate an executable page of memory.
   size_t actual_size;
   byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
@@ -81,4 +86,114 @@ TEST(AssemblerX640) {
   CHECK_EQ(2, result);
 }
 
+TEST(AssemblerX64StackOperations) {
+  // Allocate an executable page of memory.
+  size_t actual_size;
+  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+                                                 &actual_size,
+                                                 true));
+  CHECK(buffer);
+  Assembler assm(buffer, actual_size);
+
+  // Assemble a simple function that copies argument 2 and returns it.
+  // We compile without stack frame pointers, so the gdb debugger shows
+  // incorrect stack frames when debugging this function (which has them).
+  __ push(rbp);
+  __ mov(rbp, rsp);
+  __ push(rsi);  // Value at (rbp - 8)
+  __ push(rsi);  // Value at (rbp - 16)
+  __ push(rdi);  // Value at (rbp - 24)
+  __ pop(rax);
+  __ pop(rax);
+  __ pop(rax);
+  __ pop(rbp);
+  __ nop();
+  __ ret(0);
+
+  CodeDesc desc;
+  assm.GetCode(&desc);
+  // Call the function from C++.
+  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
+  CHECK_EQ(2, result);
+}
+
+TEST(AssemblerX64ArithmeticOperations) {
+  // Allocate an executable page of memory.
+  size_t actual_size;
+  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+                                                 &actual_size,
+                                                 true));
+  CHECK(buffer);
+  Assembler assm(buffer, actual_size);
+
+  // Assemble a simple function that copies argument 2 and returns it.
+  __ mov(rax, rsi);
+  __ add(rax, rdi);
+  __ ret(0);
+
+  CodeDesc desc;
+  assm.GetCode(&desc);
+  // Call the function from C++.
+  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
+  CHECK_EQ(5, result);
+}
+
+TEST(AssemblerX64MemoryOperands) {
+  // Allocate an executable page of memory.
+  size_t actual_size;
+  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+                                                 &actual_size,
+                                                 true));
+  CHECK(buffer);
+  Assembler assm(buffer, actual_size);
+
+  // Assemble a simple function that copies argument 2 and returns it.
+  __ push(rbp);
+  __ mov(rbp, rsp);
+  __ push(rsi);  // Value at (rbp - 8)
+  __ push(rsi);  // Value at (rbp - 16)
+  __ push(rdi);  // Value at (rbp - 24)
+  // const int kStackElementSize = 8;
+  //  __ mov(rax, Operand(rbp,-3 * kStackElementSize));
+  __ pop(rax);
+  __ pop(rax);
+  __ pop(rax);
+  __ pop(rbp);
+  __ nop();
+  __ ret(0);
+
+  CodeDesc desc;
+  assm.GetCode(&desc);
+  // Call the function from C++.
+  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
+  CHECK_EQ(2, result);
+}
+
+TEST(AssemblerX64ControlFlow) {
+  // Allocate an executable page of memory.
+  size_t actual_size;
+  byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
+                                                 &actual_size,
+                                                 true));
+  CHECK(buffer);
+  Assembler assm(buffer, actual_size);
+
+  // Assemble a simple function that copies argument 2 and returns it.
+  __ push(rbp);
+  __ mov(rbp, rsp);
+  __ mov(rax, rdi);
+  Label target;
+  __ jmp(&target);
+  __ mov(rax, rsi);
+  __ bind(&target);
+  __ pop(rbp);
+  __ ret(0);
+
+  CodeDesc desc;
+  assm.GetCode(&desc);
+  // Call the function from C++.
+  int result =  FUNCTION_CAST<F2>(buffer)(3, 2);
+  CHECK_EQ(3, result);
+}
+
 #undef __