Add multiplication and division to x64 assembler. Add emit_modrm() function.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2098 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
whesse@chromium.org 2009-06-03 13:30:31 +00:00
parent 92c17c34de
commit 34de62698c
2 changed files with 168 additions and 42 deletions

View File

@ -342,7 +342,7 @@ void Assembler::emit_operand(int rm, const Operand& adr) {
const unsigned length = adr.len_;
ASSERT(length > 0);
// Emit updated ModRM byte containing the given register.
// Emit updated ModR/M byte containing the given register.
pc_[0] = (adr.buf_[0] & ~0x38) | (rm << 3);
// Emit the rest of the encoded operand.
@ -367,7 +367,7 @@ void Assembler::arithmetic_op(byte opcode, Register dst, Register src) {
last_pc_ = pc_;
emit_rex_64(dst, src);
emit(opcode);
emit(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
emit_modrm(dst, src);
}
void Assembler::immediate_arithmetic_op(byte subcode,
@ -378,14 +378,14 @@ void Assembler::immediate_arithmetic_op(byte subcode,
emit_rex_64(dst);
if (is_int8(src.value_)) {
emit(0x83);
emit(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emit_modrm(subcode, dst);
emit(src.value_);
} else if (dst.is(rax)) {
emit(0x05 | (subcode << 3));
emitl(src.value_);
} else {
emit(0x81);
emit(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emit_modrm(subcode, dst);
emitl(src.value_);
}
}
@ -415,11 +415,11 @@ void Assembler::shift(Register dst, Immediate shift_amount, int subcode) {
if (shift_amount.value_ == 1) {
emit_rex_64(dst);
emit(0xD1);
emit(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emit_modrm(subcode, dst);
} else {
emit_rex_64(dst);
emit(0xC1);
emit(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emit_modrm(subcode, dst);
emit(shift_amount.value_);
}
}
@ -430,7 +430,7 @@ void Assembler::shift(Register dst, int subcode) {
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xD3);
emit(0xC0 | (subcode << 3) | (dst.code() & 0x7));
emit_modrm(subcode, dst);
}
@ -479,11 +479,11 @@ void Assembler::call(Register adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// Opcode: FF /2 r64
if (!is_uint3(adr.code())) {
if (adr.code() > 7) {
emit_rex_64(adr);
}
emit(0xFF);
emit(0xD0 | (adr.code() & 0x07));
emit_modrm(0x2, adr);
}
@ -509,7 +509,7 @@ void Assembler::dec(Register dst) {
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit(0xC8 | (dst.code() & 0x7));
emit_modrm(0x1, dst);
}
@ -538,12 +538,47 @@ void Assembler::hlt() {
}
void Assembler::idiv(Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(src);
emit(0xF7);
emit_modrm(0x7, src);
}
void Assembler::imul(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x0F);
emit(0xAF);
emit_operand(dst, src);
}
void Assembler::imul(Register dst, Register src, Immediate imm) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
if (is_int8(imm.value_)) {
emit(0x6B);
emit_modrm(dst, src);
emit(imm.value_);
} else {
emit(0x69);
emit_modrm(dst, src);
emitl(imm.value_);
}
}
void Assembler::inc(Register dst) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xFF);
emit(0xC0 | (dst.code() & 0x7));
emit_modrm(0x0, dst);
}
@ -634,11 +669,11 @@ void Assembler::jmp(Register target) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// Opcode FF/4 r64
if (!is_uint3(target.code())) {
if (target.code() > 7) {
emit_rex_64(target);
}
emit(0xFF);
emit(0xE0 | target.code() & 0x07);
emit_modrm(0x4, target);
}
@ -658,6 +693,31 @@ void Assembler::leave() {
}
void Assembler::movb(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_32(dst, src);
emit(0x8A);
emit_operand(dst, src);
}
void Assembler::movb(Register dst, Immediate imm) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_32(dst);
emit(0xC6);
emit_modrm(0x0, dst);
emit(imm.value_);
}
void Assembler::movb(const Operand& dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_32(src, dst);
emit(0x88);
emit_operand(src, dst);
}
void Assembler::movl(Register dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@ -672,7 +732,7 @@ void Assembler::movl(Register dst, Register src) {
last_pc_ = pc_;
emit_optional_rex_32(dst, src);
emit(0x8B);
emit(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
emit_modrm(dst, src);
}
@ -690,7 +750,7 @@ void Assembler::movl(Register dst, Immediate value) {
last_pc_ = pc_;
emit_optional_rex_32(dst);
emit(0xC7);
emit(0xC0 | (dst.code() & 0x7));
emit_modrm(0x0, dst);
emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
}
@ -709,7 +769,7 @@ void Assembler::movq(Register dst, Register src) {
last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x8B);
emit(0xC0 | (dst.code() & 0x7) << 3 | (src.code() & 0x7));
emit_modrm(dst, src);
}
@ -718,7 +778,7 @@ void Assembler::movq(Register dst, Immediate value) {
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xC7);
emit(0xC0 | (dst.code() & 0x7));
emit_modrm(0x0, dst);
emit(value); // Only 32-bit immediates are possible, not 8-bit immediates.
}
@ -727,7 +787,7 @@ void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xB8 | (dst.code() & 0x7));
emit(0xB8 | (dst.code() & 0x7)); // Not a ModR/M byte.
emitq(value, rmode);
}
@ -741,12 +801,21 @@ void Assembler::movq(const Operand& dst, Register src) {
}
void Assembler::mul(Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(src);
emit(0xF7);
emit_modrm(0x4, src);
}
void Assembler::neg(Register dst) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit(0xC0 | (0x3 << 3) | (dst.code() & 0x7));
emit_modrm(0x3, dst);
}
@ -771,7 +840,7 @@ void Assembler::not_(Register dst) {
last_pc_ = pc_;
emit_rex_64(dst);
emit(0xF7);
emit(0xC0 | (0x2 << 3) | (dst.code() & 0x7));
emit_modrm(0x2, dst);
}
@ -874,7 +943,7 @@ void Assembler::nop(int n) {
void Assembler::pop(Register dst) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
if (dst.code() & 0x8) {
if (dst.code() > 7) {
emit_rex_64(dst);
}
emit(0x58 | (dst.code() & 0x7));
@ -900,7 +969,7 @@ void Assembler::popfq() {
void Assembler::push(Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
if (src.code() & 0x8) {
if (src.code() > 7) {
emit_rex_64(src);
}
emit(0x50 | (src.code() & 0x7));
@ -943,11 +1012,11 @@ void Assembler::rcl(Register dst, uint8_t imm8) {
if (imm8 == 1) {
emit_rex_64(dst);
emit(0xD1);
emit(0xD0 | (dst.code() & 0x7));
emit_modrm(0x2, dst);
} else {
emit_rex_64(dst);
emit(0xC1);
emit(0xD0 | (dst.code() & 0x7));
emit_modrm(0x2, dst);
emit(imm8);
}
}
@ -967,6 +1036,26 @@ void Assembler::ret(int imm16) {
}
void Assembler::shld(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xA5);
emit_modrm(src, dst);
}
void Assembler::shrd(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(src, dst);
emit(0x0F);
emit(0xAD);
emit_modrm(src, dst);
}
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@ -977,7 +1066,7 @@ void Assembler::xchg(Register dst, Register src) {
} else {
emit_rex_64(src, dst);
emit(0x87);
emit(0xC0 | (src.code() & 0x7) << 3 | (dst.code() & 0x7));
emit_modrm(src, dst);
}
}
@ -989,11 +1078,12 @@ void Assembler::testb(Register reg, Immediate mask) {
emit(0xA8);
emit(mask);
} else {
if (reg.code() & 0x8) {
emit_rex_32(rax, reg);
if (reg.code() > 3) {
// Register is not one of al, bl, cl, dl. Its encoding needs REX.
emit_rex_32(reg);
}
emit(0xF6);
emit(0xC0 | (reg.code() & 0x3));
emit_modrm(0x0, reg);
emit(mask.value_); // Low byte emitted.
}
}
@ -1018,7 +1108,7 @@ void Assembler::testl(Register reg, Immediate mask) {
} else {
emit_optional_rex_32(rax, reg);
emit(0xF7);
emit(0xC0 | (reg.code() & 0x3));
emit_modrm(0x0, reg);
emit(mask);
}
}
@ -1034,6 +1124,24 @@ void Assembler::testl(const Operand& op, Immediate mask) {
}
void Assembler::testq(const Operand& op, Register reg) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(reg, op);
emit(0x85);
emit_operand(reg, op);
}
void Assembler::testq(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
emit_rex_64(dst, src);
emit(0x85);
emit_modrm(dst, src);
}
// Relocation information implementations
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {

View File

@ -270,7 +270,7 @@ class Operand BASE_EMBEDDED {
unsigned int len_;
RelocInfo::Mode rmode_;
// Set the ModRM byte without an encoded 'reg' register. The
// Set the ModR/M byte without an encoded 'reg' register. The
// register is encoded later as part of the emit_operand operation.
// set_modrm can be called before or after set_sib and set_disp*.
inline void set_modrm(int mod, Register rm);
@ -417,7 +417,7 @@ class Assembler : public Malloced {
// Moves
void movb(Register dst, const Operand& src);
void movb(const Operand& dst, int8_t imm8);
void movb(Register dst, Immediate imm);
void movb(const Operand& dst, Register src);
void movl(Register dst, Register src);
@ -538,16 +538,20 @@ class Assembler : public Malloced {
// Sign-extends rax into rdx:rax.
void cqo();
// Divide rdx:rax by src. Quotient in rax, remainder in rdx.
void idiv(Register src);
void imul(Register dst, Register src);
void imul(Register dst, const Operand& src);
void imul(Register dst, Register src, int32_t imm32);
// Performs the operation dst = src * imm.
void imul(Register dst, Register src, Immediate imm);
void inc(Register dst);
void inc(const Operand& dst);
void lea(Register dst, const Operand& src);
// Multiply rax by src, put the result in rdx:rax.
void mul(Register src);
void neg(Register dst);
@ -579,11 +583,11 @@ class Assembler : public Malloced {
void rcl(Register dst, uint8_t imm8);
void sbb(Register dst, const Operand& src);
// Shifts dst:src left by cl bits, affecting only dst.
void shld(Register dst, Register src);
void shld(Register dst, const Operand& src);
void shrd(Register dst, const Operand& src);
// Shifts src:dst right by cl bits, affecting only dst.
void shrd(Register dst, Register src);
// Shifts dst right, duplicating sign bit, by shift_amount bits.
// Shifting by 1 is handled efficiently.
@ -636,6 +640,8 @@ class Assembler : public Malloced {
void testb(const Operand& op, Immediate mask);
void testl(Register reg, Immediate mask);
void testl(const Operand& op, Immediate mask);
void testq(const Operand& op, Register reg);
void testq(Register dst, Register src);
void xor_(Register dst, Register src) {
arithmetic_op(0x33, dst, src);
@ -898,7 +904,7 @@ class Assembler : public Malloced {
// High bit of base goes to REX.B and high bit of index to REX.X.
// REX.W and REX.R are clear.
inline void emit_rex_32(const Operand &);
inline void emit_rex_32(const Operand& op);
// High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B.
// REX.W is cleared. If no REX bits are set, no byte is emitted.
@ -919,26 +925,38 @@ class Assembler : public Malloced {
inline void emit_optional_rex_32(const Operand& op);
// Emit the Mod/RM byte, and optionally the SIB byte and
// Emit the ModR/M byte, and optionally the SIB byte and
// 1- or 4-byte offset for a memory operand. Also encodes
// the second operand of the operation, a register or operation
// subcode, into the Mod/RM byte.
// subcode, into the reg field of the ModR/M byte.
void emit_operand(Register reg, const Operand& adr) {
emit_operand(reg.code() & 0x07, adr);
}
// Emit the Mod/RM byte, and optionally the SIB byte and
// Emit the ModR/M byte, and optionally the SIB byte and
// 1- or 4-byte offset for a memory operand. Also used to encode
// a three-byte opcode extension into the Mod/RM byte.
// a three-bit opcode extension into the ModR/M byte.
void emit_operand(int rm, const Operand& adr);
// Emit a ModR/M byte with registers coded in the reg and rm_reg fields.
void emit_modrm(Register reg, Register rm_reg) {
emit(0xC0 | (reg.code() & 0x7) << 3 | (rm_reg.code() & 0x7));
}
// Emit a ModR/M byte with an operation subcode in the reg field and
// a register in the rm_reg field.
void emit_modrm(int code, Register rm_reg) {
ASSERT((code & ~0x7) == 0);
emit(0xC0 | (code & 0x7) << 3 | (rm_reg.code() & 0x7));
}
// Emit the code-object-relative offset of the label's position
inline void emit_code_relative_offset(Label* label);
// Emit machine code for one of the operations ADD, ADC, SUB, SBC,
// AND, OR, XOR, or CMP. The encodings of these operations are all
// similar, differing just in the opcode or in the reg field of the
// Mod/RM byte.
// ModR/M byte.
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);