Commit 76d5e4e0 authored by whesse@chromium.org's avatar whesse@chromium.org

Add immediate operands and arithmetic operations to the x64 assembler.

Review URL: http://codereview.chromium.org/115816

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2069 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 58aa022d
......@@ -44,6 +44,10 @@ class Memory {
return *reinterpret_cast<int32_t*>(addr);
}
static uint64_t& uint64_at(Address addr) {
return *reinterpret_cast<uint64_t*>(addr);
}
static int& int_at(Address addr) {
return *reinterpret_cast<int*>(addr);
}
......
......@@ -44,16 +44,29 @@ Condition NegateCondition(Condition cc) {
#define EMIT(x) \
*pc_++ = (x)
void Assembler::emit(uint32_t x) {
*reinterpret_cast<uint32_t*>(pc_) = x;
void Assembler::emitl(uint32_t x) {
Memory::uint32_at(pc_) = x;
pc_ += sizeof(uint32_t);
}
void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) {
Memory::uint64_at(pc_) = x;
RecordRelocInfo(rmode, x);
}
// High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
// REX.W is set.
void Assembler::emit_rex_64(Register reg, Register rm_reg) {
EMIT(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3);
}
// The high bit of reg is used for REX.R, the high bit of op's base
// register is used for REX.B, and the high bit of op's index register
// is used for REX.X. REX.W is set.
void Assembler::emit_rex_64(Register reg, const Operand& op) {
EMIT(0x48 | (reg.code() & 0x8) >> 1 | op.rex_);
}
......
......@@ -301,23 +301,57 @@ void Assembler::emit_operand(Register reg, const Operand& adr) {
// Assembler Instruction implementations
void Assembler::add(Register dst, const Operand& src) {
void Assembler::arithmetic_op(byte opcode, Register reg, const Operand& op) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
EMIT(0x03);
emit_operand(dst, src);
emit_rex_64(reg, op);
EMIT(opcode);
emit_operand(reg, op);
}
void Assembler::add(Register dst, Register src) {
void Assembler::arithmetic_op(byte opcode, Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
EMIT(0x03);
EMIT(opcode);
EMIT(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
}
void Assembler::immediate_arithmetic_op(byte subcode,
Register dst,
Immediate src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(rax, dst);
if (is_int8(src.value_)) {
EMIT(0x83);
EMIT(0xC0 | (subcode << 3) | (dst.code() & 0x7));
EMIT(src.value_);
} else {
EMIT(0x81);
EMIT(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emitl(src.value_);
}
}
void Assembler::immediate_arithmetic_op(byte subcode,
const Operand& dst,
Immediate src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(rax, dst);
if (is_int8(src.value_)) {
EMIT(0x83);
emit_operand(Register::toRegister(subcode), dst);
EMIT(src.value_);
} else {
EMIT(0x81);
emit_operand(Register::toRegister(subcode), dst);
emitl(src.value_);
}
}
void Assembler::call(Label* L) {
EnsureSpace ensure_space(this);
......@@ -327,14 +361,14 @@ void Assembler::call(Label* L) {
if (L->is_bound()) {
int offset = L->pos() - pc_offset() - sizeof(int32_t);
ASSERT(offset <= 0);
emit(offset);
emitl(offset);
} else if (L->is_linked()) {
emit(L->pos());
emitl(L->pos());
L->link_to(pc_offset() - sizeof(int32_t));
} else {
ASSERT(L->is_unused());
int32_t current = pc_offset();
emit(current);
emitl(current);
L->link_to(current);
}
}
......@@ -407,20 +441,20 @@ void Assembler::j(Condition cc, Label* L) {
// 0000 1111 1000 tttn #32-bit disp
EMIT(0x0F);
EMIT(0x80 | cc);
emit(offs - long_size);
emitl(offs - long_size);
}
} else if (L->is_linked()) {
// 0000 1111 1000 tttn #32-bit disp
EMIT(0x0F);
EMIT(0x80 | cc);
emit(L->pos());
emitl(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);
emitl(current);
L->link_to(current);
}
}
......@@ -439,25 +473,25 @@ void Assembler::jmp(Label* L) {
} else {
// 1110 1001 #32-bit disp
EMIT(0xE9);
emit(offs - sizeof(int32_t));
emitl(offs - sizeof(int32_t));
}
} else if (L->is_linked()) {
// 1110 1001 #32-bit disp
EMIT(0xE9);
emit(L->pos());
emitl(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);
emitl(current);
L->link_to(current);
}
}
void Assembler::mov(Register dst, const Operand& src) {
void Assembler::movq(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
......@@ -466,7 +500,7 @@ void Assembler::mov(Register dst, const Operand& src) {
}
void Assembler::mov(Register dst, Register src) {
void Assembler::movq(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
......@@ -475,12 +509,32 @@ void Assembler::mov(Register dst, Register src) {
}
void Assembler::movq(Register dst, Immediate value) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(rax, dst);
EMIT(0xC7);
EMIT(0xC0 | (dst.code() & 0x7));
emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
}
void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(rax, dst);
EMIT(0xB8 | (dst.code() & 0x7));
emitq(value, rmode);
}
void Assembler::nop() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x90);
}
void Assembler::pop(Register dst) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
......
......@@ -63,6 +63,10 @@ namespace internal {
//
struct Register {
static Register toRegister(int code) {
Register r = {code};
return r;
}
bool is_valid() const { return 0 <= code_ && code_ < 16; }
bool is(Register reg) const { return code_ == reg.code_; }
// The byte-register distinction of ai32 has dissapeared.
......@@ -207,34 +211,11 @@ inline Hint NegateHint(Hint hint) {
class Immediate BASE_EMBEDDED {
public:
inline explicit Immediate(int64_t x);
inline explicit Immediate(const char* s);
inline explicit Immediate(const ExternalReference& ext);
inline explicit Immediate(Handle<Object> handle);
explicit Immediate(int32_t value) : value_(value) {}
inline explicit Immediate(Smi* value);
static Immediate CodeRelativeOffset(Label* label) {
return Immediate(label);
}
bool is_zero() const { return x_ == 0 && rmode_ == RelocInfo::NONE; }
bool is_int8() const {
return -128 <= x_ && x_ < 128 && rmode_ == RelocInfo::NONE;
}
bool is_int16() const {
return -32768 <= x_ && x_ < 32768 && rmode_ == RelocInfo::NONE;
}
bool is_int32() const {
return V8_INT64_C(-2147483648) <= x_
&& x_ < V8_INT64_C(2147483648)
&& rmode_ == RelocInfo::NONE;
}
private:
inline explicit Immediate(Label* value) { UNIMPLEMENTED(); }
int64_t x_;
RelocInfo::Mode rmode_;
int32_t value_;
friend class Assembler;
};
......@@ -422,21 +403,27 @@ class Assembler : public Malloced {
void leave();
// Moves
void mov_b(Register dst, const Operand& src);
void mov_b(const Operand& dst, int8_t imm8);
void mov_b(const Operand& dst, Register src);
void mov_w(Register dst, const Operand& src);
void mov_w(const Operand& dst, Register src);
void mov(Register dst, int32_t imm32);
void mov(Register dst, const Immediate& x);
void mov(Register dst, Handle<Object> handle);
void mov(Register dst, const Operand& src);
void mov(Register dst, Register src);
void mov(const Operand& dst, const Immediate& x);
void mov(const Operand& dst, Handle<Object> handle);
void mov(const Operand& dst, Register src);
void movb(Register dst, const Operand& src);
void movb(const Operand& dst, int8_t imm8);
void movb(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);
void movq(Register dst, Register src);
void movq(const Operand& dst, const Immediate& x);
void movq(const Operand& dst, Register src);
// New x64 instructions to load a 64-bit immediate into a register.
// All 64-bit immediates must have a relocation mode.
void movq(Register dst, void* ptr, RelocInfo::Mode rmode);
void movq(Register dst, int64_t value, RelocInfo::Mode rmode);
void movq(Register dst, const char* s, RelocInfo::Mode rmode);
void movq(Register dst, const ExternalReference& ext, RelocInfo::Mode rmode);
void movq(Register dst, Handle<Object> handle, RelocInfo::Mode rmode);
// New x64 instruction to load from an immediate 64-bit pointer into RAX.
void load_rax(void* ptr, RelocInfo::Mode rmode);
void movsx_b(Register dst, const Operand& src);
......@@ -455,12 +442,46 @@ class Assembler : public Malloced {
void xchg(Register dst, Register src);
// Arithmetics
void adc(Register dst, int32_t imm32);
void adc(Register dst, const Operand& src);
void add(Register dst, Register src) {
arithmetic_op(0x03, dst, src);
}
void add(Register dst, const Operand& src) {
arithmetic_op(0x03, dst, src);
}
void add(const Operand& dst, Register src) {
arithmetic_op(0x01, src, dst);
}
void add(Register dst, Immediate src) {
immediate_arithmetic_op(0x0, dst, src);
}
void add(const Operand& dst, Immediate src) {
immediate_arithmetic_op(0x0, dst, src);
}
void cmp(Register dst, Register src) {
arithmetic_op(0x3B, dst, src);
}
void cmp(Register dst, const Operand& src) {
arithmetic_op(0x3B, dst, src);
}
void cmp(const Operand& dst, Register src) {
arithmetic_op(0x39, src, dst);
}
void cmp(Register dst, Immediate src) {
immediate_arithmetic_op(0x7, dst, src);
}
void cmp(const Operand& dst, Immediate src) {
immediate_arithmetic_op(0x7, dst, src);
}
void add(Register dst, Register src);
void add(Register dst, const Operand& src);
void add(const Operand& dst, const Immediate& x);
void and_(Register dst, int32_t imm32);
void and_(Register dst, const Operand& src);
......@@ -471,10 +492,6 @@ class Assembler : public Malloced {
void cmpb_al(const Operand& op);
void cmpw_ax(const Operand& op);
void cmpw(const Operand& op, Immediate imm16);
void cmp(Register reg, int32_t imm32);
void cmp(Register reg, Handle<Object> handle);
void cmp(Register reg, const Operand& op);
void cmp(const Operand& op, const Immediate& imm);
void dec_b(Register dst);
......@@ -716,18 +733,22 @@ class Assembler : public Malloced {
// code emission
void GrowBuffer();
inline void emit(uint32_t x);
inline void emitl(uint32_t x);
inline void emit(Handle<Object> handle);
inline void emit(uint32_t x, RelocInfo::Mode rmode);
inline void emit(const Immediate& x);
inline void emit_w(const Immediate& x);
inline void emitq(uint64_t x, RelocInfo::Mode rmode);
void emit(Immediate x) { emitl(x.value_); }
// Emits a REX prefix that encodes a 64-bit operand size and
// the top bit of both register codes.
// High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
// REX.W is set.
inline void emit_rex_64(Register reg, Register rm_reg);
// Emits a REX prefix that encodes a 64-bit operand size and
// the top bit of the destination, index, and base register codes.
// The high bit of reg is used for REX.R, the high bit of op's base
// register is used for REX.B, and the high bit of op's index register
// is used for REX.X. REX.W is set.
inline void emit_rex_64(Register reg, const Operand& op);
// Emit the code-object-relative offset of the label's position
......@@ -740,6 +761,11 @@ class Assembler : public Malloced {
// with a given destination expression and an immediate operand. It attempts
// to use the shortest encoding possible.
// sel specifies the /n in the modrm byte (see the Intel PRM).
void arithmetic_op(byte opcode, Register dst, Register src);
void arithmetic_op(byte opcode, Register reg, const Operand& op);
void immediate_arithmetic_op(byte subcode, Register dst, Immediate src);
void immediate_arithmetic_op(byte subcode, const Operand& dst, Immediate src);
void emit_arith(int sel, Operand dst, const Immediate& x);
void emit_operand(Register reg, const Operand& adr);
......@@ -752,7 +778,9 @@ class Assembler : public Malloced {
void link_to(Label* L, Label* appendix);
// record reloc info for current pc_
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0) {
UNIMPLEMENTED();
}
friend class CodePatcher;
friend class EnsureSpace;
......
......@@ -39,6 +39,7 @@ using v8::internal::byte;
using v8::internal::OS;
using v8::internal::Assembler;
using v8::internal::Operand;
using v8::internal::Immediate;
using v8::internal::Label;
using v8::internal::rax;
using v8::internal::rsi;
......@@ -47,6 +48,9 @@ using v8::internal::rbp;
using v8::internal::rsp;
using v8::internal::FUNCTION_CAST;
using v8::internal::CodeDesc;
using v8::internal::less_equal;
using v8::internal::not_equal;
using v8::internal::greater;
// Test the x64 assembler by compiling some simple functions into
......@@ -75,7 +79,7 @@ TEST(AssemblerX64ReturnOperation) {
Assembler assm(buffer, actual_size);
// Assemble a simple function that copies argument 2 and returns it.
__ mov(rax, rsi);
__ movq(rax, rsi);
__ nop();
__ ret(0);
......@@ -99,7 +103,7 @@ TEST(AssemblerX64StackOperations) {
// 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);
__ movq(rbp, rsp);
__ push(rsi); // Value at (rbp - 8)
__ push(rsi); // Value at (rbp - 16)
__ push(rdi); // Value at (rbp - 24)
......@@ -127,7 +131,7 @@ TEST(AssemblerX64ArithmeticOperations) {
Assembler assm(buffer, actual_size);
// Assemble a simple function that copies argument 2 and returns it.
__ mov(rax, rsi);
__ movq(rax, rsi);
__ add(rax, rdi);
__ ret(0);
......@@ -149,12 +153,12 @@ TEST(AssemblerX64MemoryOperands) {
// Assemble a simple function that copies argument 2 and returns it.
__ push(rbp);
__ mov(rbp, rsp);
__ movq(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));
__ movq(rax, Operand(rbp, -3 * kStackElementSize));
__ pop(rsi);
__ pop(rsi);
__ pop(rsi);
......@@ -180,11 +184,11 @@ TEST(AssemblerX64ControlFlow) {
// Assemble a simple function that copies argument 2 and returns it.
__ push(rbp);
__ mov(rbp, rsp);
__ mov(rax, rdi);
__ movq(rbp, rsp);
__ movq(rax, rdi);
Label target;
__ jmp(&target);
__ mov(rax, rsi);
__ movq(rax, rsi);
__ bind(&target);
__ pop(rbp);
__ ret(0);
......@@ -196,4 +200,52 @@ TEST(AssemblerX64ControlFlow) {
CHECK_EQ(3, result);
}
TEST(AssemblerX64LoopImmediates) {
// 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 two loops using rax as counter, and verify the ending counts.
Label Fail;
__ movq(rax, Immediate(-3));
Label Loop1_test;
Label Loop1_body;
__ jmp(&Loop1_test);
__ bind(&Loop1_body);
__ add(rax, Immediate(7));
__ bind(&Loop1_test);
__ cmp(rax, Immediate(20));
__ j(less_equal, &Loop1_body);
// Did the loop terminate with the expected value?
__ cmp(rax, Immediate(25));
__ j(not_equal, &Fail);
Label Loop2_test;
Label Loop2_body;
__ movq(rax, Immediate(0x11FEED00));
__ jmp(&Loop2_test);
__ bind(&Loop2_body);
__ add(rax, Immediate(-0x1100));
__ bind(&Loop2_test);
__ cmp(rax, Immediate(0x11FE8000));
__ j(greater, &Loop2_body);
// Did the loop terminate with the expected value?
__ cmp(rax, Immediate(0x11FE7600));
__ j(not_equal, &Fail);
__ movq(rax, Immediate(1));
__ ret(0);
__ bind(&Fail);
__ movq(rax, Immediate(0));
__ ret(0);
CodeDesc desc;
assm.GetCode(&desc);
// Call the function from C++.
int result = FUNCTION_CAST<F0>(buffer)();
CHECK_EQ(1, result);
}
#undef __
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment