[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}
This commit is contained in:
bmeurer 2015-02-06 04:48:15 -08:00 committed by Commit bot
parent 30787d4e56
commit 008f4732f7
5 changed files with 204 additions and 26 deletions

View File

@ -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<int32_t>(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<int32_t>(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<int64_t*>(&buf_[len_]);
*p = disp;
len_ += sizeof(disp);
}
} } // namespace v8::internal
#endif // V8_X64_ASSEMBLER_X64_INL_H_

View File

@ -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<intptr_t>(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<intptr_t>(buffer_ + pos);
*reinterpret_cast<intptr_t*>(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<intptr_t>(buffer_ + pos);
*reinterpret_cast<intptr_t*>(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<intptr_t*>(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<intptr_t*>(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;
*pc_++ = 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;
// Recognize RIP relative addressing.
if (adr.buf_[0] == 5) {
DCHECK_EQ(9u, length);
Label* label = *reinterpret_cast<Label* const*>(&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) {

View File

@ -37,6 +37,8 @@
#ifndef V8_X64_ASSEMBLER_X64_H_
#define V8_X64_ASSEMBLER_X64_H_
#include <deque>
#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<int> internal_reference_positions_;
List< Handle<Code> > code_targets_;
PositionsRecorder positions_recorder_;

View File

@ -503,7 +503,7 @@ int DisassemblerX64::PrintRightOperandHelper(
case 0:
if ((rm & 7) == 5) {
int32_t disp = *reinterpret_cast<int32_t*>(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, &regop, &rm);
AppendToBuffer("%s,", NameOfCPURegister(regop));
current += PrintRightOperand(current);
} else if (opcode == 0x0B) {
AppendToBuffer("ud2");
} else {
UnimplementedInstruction();
}

View File

@ -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 <stdlib.h>
#include <cstdlib>
#include <iostream>
#include "src/v8.h"
@ -1186,4 +1187,100 @@ TEST(AssemblerX64FMA_ss) {
F8 f = FUNCTION_CAST<F8>(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<Isolate*>(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> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef OBJECT_PRINT
code->Print(std::cout);
#endif
F1 f = FUNCTION_CAST<F1>(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<Isolate*>(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> code = isolate->factory()->NewCode(
desc, Code::ComputeFlags(Code::STUB), Handle<Code>());
#ifdef OBJECT_PRINT
code->Print(std::cout);
#endif
F1 f = FUNCTION_CAST<F1>(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 __