From 734f1fd13517f79882d096f15e1ccfd6cf624d72 Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Tue, 2 Jun 2009 07:21:05 +0000 Subject: [PATCH] X64: Added jmp and call and nop(n) to X64 assembler. Review URL: http://codereview.chromium.org/115920 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2085 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/assembler.h | 1 + src/x64/assembler-x64-inl.h | 21 ++++++- src/x64/assembler-x64.cc | 116 +++++++++++++++++++++++++++++++++++- src/x64/assembler-x64.h | 52 +++++++++++----- 4 files changed, 169 insertions(+), 21 deletions(-) diff --git a/src/assembler.h b/src/assembler.h index 16e81477a..570524e06 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -437,6 +437,7 @@ static inline bool is_uintn(int x, int n) { return (x & -(1 << n)) == 0; } +static inline bool is_uint2(int x) { return is_uintn(x, 2); } static inline bool is_uint3(int x) { return is_uintn(x, 3); } static inline bool is_uint4(int x) { return is_uintn(x, 4); } static inline bool is_uint5(int x) { return is_uintn(x, 5); } diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h index 6d3ed1544..ad349345a 100644 --- a/src/x64/assembler-x64-inl.h +++ b/src/x64/assembler-x64-inl.h @@ -70,8 +70,23 @@ void Assembler::emit_rex_64(Register reg, const Operand& op) { } +// The high bit of the register is used for REX.B. +// REX.W is set and REX.R and REX.X are clear. +void Assembler::emit_rex_64(Register rm_reg) { + ASSERT_EQ(rm_reg.code() & 0x0f, rm_reg.code()); + emit(0x48 | (rm_reg.code() >> 3)); +} + + +// 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 and REX.R clear. +void Assembler::emit_rex_64(const Operand& op) { + emit(0x48 | op.rex_); +} + + // 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. +// REX.W and REX.X are clear. void Assembler::emit_rex_32(Register reg, Register rm_reg) { emit(0x40 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); } @@ -222,7 +237,7 @@ Operand::Operand(Register base, int32_t disp) { len_ = 1; if (base.is(rsp) || base.is(r12)) { // SIB byte is needed to encode (rsp + offset) or (r12 + offset). - set_sib(times_1, rsp, base); + set_sib(kTimes1, rsp, base); } if (disp == 0 && !base.is(rbp) && !base.is(r13)) { @@ -246,7 +261,7 @@ void Operand::set_modrm(int mod, Register rm) { void Operand::set_sib(ScaleFactor scale, Register index, Register base) { ASSERT(len_ == 1); - ASSERT((scale & -4) == 0); + ASSERT(is_uint2(scale)); // Use SIB with no index register only for base rsp or r12. ASSERT(!index.is(rsp) || base.is(rsp) || base.is(r12)); buf_[1] = scale << 6 | (index.code() & 0x7) << 3 | (base.code() & 0x7); diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index acea71319..86e116533 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -271,12 +271,13 @@ void Assembler::GrowBuffer() { } -void Assembler::emit_operand(Register reg, const Operand& adr) { +void Assembler::emit_operand(int rm, const Operand& adr) { + ASSERT_EQ(rm & 0x07, rm); const unsigned length = adr.len_; ASSERT(length > 0); // Emit updated ModRM byte containing the given register. - pc_[0] = (adr.buf_[0] & ~0x38) | ((reg.code() && 0x7) << 3); + pc_[0] = (adr.buf_[0] & ~0x38) | (rm << 3); // Emit the rest of the encoded operand. for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i]; @@ -362,6 +363,18 @@ void Assembler::call(Label* L) { } +void Assembler::call(Register adr) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // Opcode: FF /2 r64 + if (!is_uint3(adr.code())) { + emit_rex_64(adr); + } + emit(0xFF); + emit(0xD0 | (adr.code() & 0x07)); +} + + void Assembler::dec(Register dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -479,6 +492,18 @@ void Assembler::jmp(Label* L) { } +void Assembler::jmp(Register target) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + // Opcode FF/4 r64 + if (!is_uint3(target.code())) { + emit_rex_64(target); + } + emit(0xFF); + emit(0xE0 | target.code() & 0x07); +} + + void Assembler::movq(Register dst, const Operand& src) { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -559,6 +584,93 @@ void Assembler::not_(const Operand& dst) { } +void Assembler::nop(int n) { + // The recommended muti-byte sequences of NOP instructions from the Intel 64 + // and IA-32 Architectures Software Developer's Manual. + // + // Length Assembly Byte Sequence + // 2 bytes 66 NOP 66 90H + // 3 bytes NOP DWORD ptr [EAX] 0F 1F 00H + // 4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H + // 5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H + // 6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H + // 7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H + // 8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H + // 9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 66 0F 1F 84 00 00 00 00 + // 00000000H] 00H + + ASSERT(1 <= n); + ASSERT(n <= 9); + EnsureSpace ensure_space(this); + last_pc_ = pc_; + switch (n) { + case 1: + emit(0x90); + return; + case 2: + emit(0x66); + emit(0x90); + return; + case 3: + emit(0x0f); + emit(0x1f); + emit(0x00); + return; + case 4: + emit(0x0f); + emit(0x1f); + emit(0x40); + emit(0x00); + return; + case 5: + emit(0x0f); + emit(0x1f); + emit(0x44); + emit(0x00); + emit(0x00); + return; + case 6: + emit(0x66); + emit(0x0f); + emit(0x1f); + emit(0x44); + emit(0x00); + emit(0x00); + return; + case 7: + emit(0x0f); + emit(0x1f); + emit(0x80); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + case 8: + emit(0x0f); + emit(0x1f); + emit(0x84); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + case 9: + emit(0x66); + emit(0x0f); + emit(0x1f); + emit(0x84); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + emit(0x00); + return; + } +} + + void Assembler::pop(Register dst) { EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 06a7c4070..170e6d675 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -225,10 +225,12 @@ class Immediate BASE_EMBEDDED { // Machine instruction Operands enum ScaleFactor { - times_1 = 0, - times_2 = 1, - times_4 = 2, - times_8 = 3 + kTimes1 = 0, + kTimes2 = 1, + kTimes4 = 2, + kTimes8 = 3, + kTimesIntSize = kTimes4, + kTimesPointerSize = kTimes8 }; @@ -626,6 +628,7 @@ class Assembler : public Malloced { void hlt(); void int3(); void nop(); + void nop(int n); void rdtsc(); void ret(int imm16); @@ -647,16 +650,18 @@ class Assembler : public Malloced { void bind(Label* L); // binds an unbound label L to the current code position // Calls + // Call near relative 32-bit displacement, relative to next instruction. void call(Label* L); - void call(byte* entry, RelocInfo::Mode rmode); - void call(const Operand& adr); - void call(Handle code, RelocInfo::Mode rmode); + + // Call near absolute indirect, address in register + void call(Register adr); // Jumps + // Jump short or near relative. void jmp(Label* L); // unconditional jump to L - void jmp(byte* entry, RelocInfo::Mode rmode); - void jmp(const Operand& adr); - void jmp(Handle code, RelocInfo::Mode rmode); + + // Jump near absolute indirect (r64) + void jmp(Register adr); // Conditional jumps void j(Condition cc, Label* L); @@ -815,7 +820,6 @@ class Assembler : public Malloced { // 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); - void emit_rex_64(Register rm_reg) { emit_rex_64(rax, 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. @@ -823,10 +827,22 @@ class Assembler : public Malloced { // 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); - void emit_rex_64(const Operand& op) { emit_rex_64(rax, op); } + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of the register code. + // The high bit of register is used for REX.B. + // REX.W is set and REX.R and REX.X are clear. + inline void emit_rex_64(Register rm_reg); + + // Emits a REX prefix that encodes a 64-bit operand size and + // the top bit of the index and base register codes. + // 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 and REX.R clear. + inline void emit_rex_64(const Operand& op); // High bit of reg goes to REX.R, high bit of rm_reg goes to REX.B. - // REX.W is set. + // REX.W is clear. inline void emit_rex_32(Register reg, Register rm_reg); // The high bit of reg is used for REX.R, the high bit of op's base @@ -848,11 +864,15 @@ class Assembler : public Malloced { // 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. - void emit_operand(Register reg, const Operand& adr); - void emit_operand(int op_subcode, const Operand& adr) { - emit_operand(Register::toRegister(op_subcode), adr); + 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 + // 1- or 4-byte offset for a memory operand. Also used to encode + // a three-byte opcode extension into the Mod/RM byte. + void emit_operand(int rm, const Operand& adr); + // Emit the code-object-relative offset of the label's position inline void emit_code_relative_offset(Label* label); -- 2.34.1