[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:
parent
30787d4e56
commit
008f4732f7
@ -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_
|
||||
|
@ -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) {
|
||||
|
@ -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_;
|
||||
|
@ -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, ®op, &rm);
|
||||
AppendToBuffer("%s,", NameOfCPURegister(regop));
|
||||
current += PrintRightOperand(current);
|
||||
} else if (opcode == 0x0B) {
|
||||
AppendToBuffer("ud2");
|
||||
} else {
|
||||
UnimplementedInstruction();
|
||||
}
|
||||
|
@ -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 __
|
||||
|
Loading…
Reference in New Issue
Block a user