From 008f4732f78f71d8d37bc574b75d5bccff94025b Mon Sep 17 00:00:00 2001 From: bmeurer Date: Fri, 6 Feb 2015 04:48:15 -0800 Subject: [PATCH] [x64] Assembler support for internal references and RIP relative addressing. R=dcarney@chromium.org BUG=v8:3872 LOG=n Review URL: https://codereview.chromium.org/887013003 Cr-Commit-Position: refs/heads/master@{#26486} --- src/x64/assembler-x64-inl.h | 9 +++- src/x64/assembler-x64.cc | 101 +++++++++++++++++++++++++++++++------- src/x64/assembler-x64.h | 19 +++++-- src/x64/disasm-x64.cc | 4 +- test/cctest/test-assembler-x64.cc | 99 ++++++++++++++++++++++++++++++++++++- 5 files changed, 205 insertions(+), 27 deletions(-) diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h index caf7af6..64c71cf 100644 --- a/src/x64/assembler-x64-inl.h +++ b/src/x64/assembler-x64-inl.h @@ -292,7 +292,7 @@ void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) { bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH; if (IsInternalReference(rmode_)) { // absolute code pointer inside code object moves with the code object. - Memory::Address_at(pc_) += static_cast(delta); + Memory::Address_at(pc_) += delta; if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(Address)); } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { Memory::int32_at(pc_) -= static_cast(delta); @@ -621,7 +621,12 @@ void Operand::set_disp32(int disp) { len_ += sizeof(int32_t); } - +void Operand::set_disp64(int64_t disp) { + DCHECK_EQ(1, len_); + int64_t* p = reinterpret_cast(&buf_[len_]); + *p = disp; + len_ += sizeof(disp); +} } } // namespace v8::internal #endif // V8_X64_ASSEMBLER_X64_INL_H_ diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 3559964..597aa31 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -219,6 +219,13 @@ Operand::Operand(Register index, } +Operand::Operand(Label* label) : rex_(0), len_(1) { + DCHECK_NOT_NULL(label); + set_modrm(0, rbp); + set_disp64(reinterpret_cast(label)); +} + + Operand::Operand(const Operand& operand, int32_t offset) { DCHECK(operand.len_ >= 1); // Operand encodes REX ModR/M [SIB] [Disp]. @@ -365,15 +372,30 @@ void Assembler::bind_to(Label* L, int pos) { 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); + if (current >= 4 && long_at(current - 4) == 0) { + // Absolute address. + intptr_t imm64 = reinterpret_cast(buffer_ + pos); + *reinterpret_cast(addr_at(current - 4)) = imm64; + internal_reference_positions_.push_back(current - 4); + } else { + // 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); + if (current >= 4 && long_at(current - 4) == 0) { + // Absolute address. + intptr_t imm64 = reinterpret_cast(buffer_ + pos); + *reinterpret_cast(addr_at(current - 4)) = imm64; + internal_reference_positions_.push_back(current - 4); + } else { + // Relative address, relative to point after address. + int imm32 = pos - (current + sizeof(int32_t)); + long_at_put(current, imm32); + } } while (L->is_near_linked()) { int fixup_pos = L->near_link_pos(); @@ -441,15 +463,10 @@ void Assembler::GrowBuffer() { reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, reloc_info_writer.last_pc() + pc_delta); - // Relocate runtime entries. - for (RelocIterator it(desc); !it.done(); it.next()) { - RelocInfo::Mode rmode = it.rinfo()->rmode(); - if (rmode == RelocInfo::INTERNAL_REFERENCE) { - intptr_t* p = reinterpret_cast(it.rinfo()->pc()); - if (*p != 0) { // 0 means uninitialized. - *p += pc_delta; - } - } + // Relocate internal references. + for (auto pos : internal_reference_positions_) { + intptr_t* p = reinterpret_cast(buffer_ + pos); + *p += pc_delta; } DCHECK(!buffer_overflow()); @@ -463,11 +480,29 @@ void Assembler::emit_operand(int code, const Operand& adr) { // Emit updated ModR/M byte containing the given register. DCHECK((adr.buf_[0] & 0x38) == 0); - pc_[0] = adr.buf_[0] | code << 3; - - // Emit the rest of the encoded operand. - for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i]; - pc_ += length; + *pc_++ = adr.buf_[0] | code << 3; + + // Recognize RIP relative addressing. + if (adr.buf_[0] == 5) { + DCHECK_EQ(9u, length); + Label* label = *reinterpret_cast(&adr.buf_[1]); + if (label->is_bound()) { + int offset = label->pos() - pc_offset() - sizeof(int32_t); + DCHECK_GE(0, offset); + emitl(offset); + } else if (label->is_linked()) { + emitl(label->pos()); + label->link_to(pc_offset() - sizeof(int32_t)); + } else { + DCHECK(label->is_unused()); + int32_t current = pc_offset(); + emitl(current); + label->link_to(current); + } + } else { + // Emit the rest of the encoded operand. + for (unsigned i = 1; i < length; i++) *pc_++ = adr.buf_[i]; + } } @@ -1815,6 +1850,13 @@ void Assembler::ret(int imm16) { } +void Assembler::ud2() { + EnsureSpace ensure_space(this); + emit(0x0F); + emit(0x0B); +} + + void Assembler::setcc(Condition cc, Register reg) { if (cc > last_condition) { movb(reg, Immediate(cc == always ? 1 : 0)); @@ -3347,6 +3389,27 @@ void Assembler::dd(uint32_t data) { } +void Assembler::dq(Label* label) { + EnsureSpace ensure_space(this); + if (label->is_bound()) { + internal_reference_positions_.push_back(pc_offset()); + emitp(buffer_ + label->pos(), RelocInfo::INTERNAL_REFERENCE); + } else { + RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE); + emitl(0); // Zero for the first 32bit marks it as 64bit absolute address. + if (label->is_linked()) { + emitl(label->pos()); + label->link_to(pc_offset() - sizeof(int32_t)); + } else { + DCHECK(label->is_unused()); + int32_t current = pc_offset(); + emitl(current); + label->link_to(current); + } + } +} + + // Relocation information implementations. void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index b973495..330daf6 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -37,6 +37,8 @@ #ifndef V8_X64_ASSEMBLER_X64_H_ #define V8_X64_ASSEMBLER_X64_H_ +#include + #include "src/serialize.h" namespace v8 { @@ -408,6 +410,9 @@ class Operand BASE_EMBEDDED { // this must not overflow. Operand(const Operand& base, int32_t offset); + // [rip + disp/r] + explicit Operand(Label* label); + // Checks whether either base or index register is the given register. // Does not check the "reg" part of the Operand. bool AddressUsesRegister(Register reg) const; @@ -421,7 +426,7 @@ class Operand BASE_EMBEDDED { private: byte rex_; - byte buf_[6]; + byte buf_[9]; // The number of bytes of buf_ in use. byte len_; @@ -437,6 +442,7 @@ class Operand BASE_EMBEDDED { // Needs to be called after set_sib, not before it. inline void set_disp8(int disp); inline void set_disp32(int disp); + inline void set_disp64(int64_t disp); // for labels. friend class Assembler; }; @@ -888,6 +894,7 @@ class Assembler : public AssemblerBase { void int3(); void nop(); void ret(int imm16); + void ud2(); void setcc(Condition cc, Register reg); // Label operations & relative jumps (PPUM Appendix D) @@ -934,6 +941,7 @@ class Assembler : public AssemblerBase { // Jump near absolute indirect (r64) void jmp(Register adr); + void jmp(const Operand& src); // Conditional jumps void j(Condition cc, @@ -1344,6 +1352,7 @@ class Assembler : public AssemblerBase { // Used for inline tables, e.g., jump-tables. void db(uint8_t data); void dd(uint32_t data); + void dq(Label* label); PositionsRecorder* positions_recorder() { return &positions_recorder_; } @@ -1371,9 +1380,6 @@ class Assembler : public AssemblerBase { // Call near indirect void call(const Operand& operand); - // Jump near absolute indirect (m64) - void jmp(const Operand& src); - private: byte* addr_at(int pos) { return buffer_ + pos; } uint32_t long_at(int pos) { @@ -1811,6 +1817,11 @@ class Assembler : public AssemblerBase { // code generation RelocInfoWriter reloc_info_writer; + // Internal reference positions, required for (potential) patching in + // GrowBuffer(); contains only those internal references whose labels + // are already bound. + std::deque internal_reference_positions_; + List< Handle > code_targets_; PositionsRecorder positions_recorder_; diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index ded6c33..bed99d1 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -503,7 +503,7 @@ int DisassemblerX64::PrintRightOperandHelper( case 0: if ((rm & 7) == 5) { int32_t disp = *reinterpret_cast(modrmp + 1); - AppendToBuffer("[0x%x]", disp); + AppendToBuffer("[rip+0x%x]", disp); return 5; } else if ((rm & 7) == 4) { // Codes for SIB byte. @@ -1500,6 +1500,8 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { get_modrm(*current, &mod, ®op, &rm); AppendToBuffer("%s,", NameOfCPURegister(regop)); current += PrintRightOperand(current); + } else if (opcode == 0x0B) { + AppendToBuffer("ud2"); } else { UnimplementedInstruction(); } diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc index 324572a..f5d59de 100644 --- a/test/cctest/test-assembler-x64.cc +++ b/test/cctest/test-assembler-x64.cc @@ -25,7 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include +#include +#include #include "src/v8.h" @@ -1186,4 +1187,100 @@ TEST(AssemblerX64FMA_ss) { F8 f = FUNCTION_CAST(code->entry()); CHECK_EQ(0, f(9.26621069e-05f, -2.4607749f, -1.09587872f)); } + + +TEST(AssemblerX64JumpTables1) { + // Test jump tables with forward jumps. + CcTest::InitializeVM(); + Isolate* isolate = reinterpret_cast(CcTest::isolate()); + HandleScope scope(isolate); + MacroAssembler assm(isolate, nullptr, 0); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + Label done, table; + __ leaq(arg2, Operand(&table)); + __ jmp(Operand(arg2, arg1, times_8, 0)); + __ ud2(); + __ bind(&table); + for (int i = 0; i < kNumCases; ++i) { + __ dq(&labels[i]); + } + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ movq(rax, Immediate(values[i])); + __ jmp(&done); + } + + __ bind(&done); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Handle code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + + F1 f = FUNCTION_CAST(code->entry()); + for (int i = 0; i < kNumCases; ++i) { + int res = f(i); + PrintF("f(%d) = %d\n", i, res); + CHECK_EQ(values[i], res); + } +} + + +TEST(AssemblerX64JumpTables2) { + // Test jump tables with backwards jumps. + CcTest::InitializeVM(); + Isolate* isolate = reinterpret_cast(CcTest::isolate()); + HandleScope scope(isolate); + MacroAssembler assm(isolate, nullptr, 0); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + Label done, table; + __ leaq(arg2, Operand(&table)); + __ jmp(Operand(arg2, arg1, times_8, 0)); + __ ud2(); + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ movq(rax, Immediate(values[i])); + __ jmp(&done); + } + + __ bind(&done); + __ ret(0); + + __ bind(&table); + for (int i = 0; i < kNumCases; ++i) { + __ dq(&labels[i]); + } + + CodeDesc desc; + assm.GetCode(&desc); + Handle code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + + F1 f = FUNCTION_CAST(code->entry()); + for (int i = 0; i < kNumCases; ++i) { + int res = f(i); + PrintF("f(%d) = %d\n", i, res); + CHECK_EQ(values[i], res); + } +} + #undef __ -- 2.7.4