#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);
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");
}
+// Assembler Instruction implementations
+
void Assembler::add(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
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);
+ }
}
}
+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_;
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));
}
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);
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;
#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,
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 __