runtime/assembler-x64.cpp (839 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) // Copyright (c) 2013, the Dart project authors and Facebook, Inc. and its // affiliates. Please see the AUTHORS-Dart file for details. All rights // reserved. Use of this source code is governed by a BSD-style license that // can be found in the LICENSE-Dart file. #include "assembler-x64.h" #include <cstdarg> #include <cstdio> #include <cstring> #include "globals.h" namespace py { namespace x64 { void Assembler::initializeMemoryWithBreakpoints(uword data, word length) { std::memset(reinterpret_cast<void*>(data), 0xcc, length); } void Assembler::call(Label* label) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); static const int size = 5; emitUint8(0xe8); emitLabel(label, size); } void Assembler::pushq(Register reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(reg, REX_NONE); emitUint8(0x50 | (reg & 7)); } void Assembler::pushq(Immediate imm) { if (imm.isInt8()) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0x6a); emitUint8(imm.value() & 0xff); } else { DCHECK(imm.isInt32(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0x68); emitImmediate(imm); } } void Assembler::popq(Register reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(reg, REX_NONE); emitUint8(0x58 | (reg & 7)); } void Assembler::setcc(Condition condition, Register dst) { DCHECK(dst != kNoRegister, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (dst >= 8) { emitUint8(REX_PREFIX | (((dst & 0x08) != 0) ? REX_B : REX_NONE)); } emitUint8(0x0f); emitUint8(0x90 + condition); emitUint8(0xc0 + (dst & 0x07)); } void Assembler::emitQ(int reg, Address address, int opcode, int prefix2, int prefix1) { DCHECK(reg <= XMM15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitOperandREX(reg, address, REX_W); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitOperand(reg & 7, address); } void Assembler::emitB(Register reg, Address address, int opcode, int prefix2, int prefix1) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitOperandREX(reg, address, byteRegisterREX(reg)); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitOperand(reg & 7, address); } void Assembler::emitL(int reg, Address address, int opcode, int prefix2, int prefix1) { DCHECK(reg <= XMM15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitOperandREX(reg, address, REX_NONE); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitOperand(reg & 7, address); } void Assembler::emitW(Register reg, Address address, int opcode, int prefix2, int prefix1) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitOperandSizeOverride(); emitOperandREX(reg, address, REX_NONE); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitOperand(reg & 7, address); } void Assembler::movl(Register dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(dst); emitOperandREX(0, operand, REX_NONE); emitUint8(0xc7); emitOperand(0, operand); DCHECK(imm.isInt32(), "assert()"); emitImmediate(imm); } void Assembler::movl(Address dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(dst); emitOperandREX(0, dst, REX_NONE); emitUint8(0xc7); emitOperand(0, dst); DCHECK(imm.isInt32(), "assert()"); emitImmediate(imm); } void Assembler::movb(Address dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(0, dst, REX_NONE); emitUint8(0xc6); emitOperand(0, dst); DCHECK(imm.isInt8(), "assert()"); emitUint8(imm.value() & 0xff); } void Assembler::movw(Register /*dst*/, Address /*src*/) { UNIMPLEMENTED("Use movzxw or movsxw instead."); } void Assembler::movw(Address dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandSizeOverride(); emitOperandREX(0, dst, REX_NONE); emitUint8(0xc7); emitOperand(0, dst); emitUint8(imm.value() & 0xff); emitUint8((imm.value() >> 8) & 0xff); } void Assembler::leaq(Register dst, Label* label) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); // Emit RIP-relative lea emitUint8(REX_PREFIX | REX_W | (dst > 7 ? REX_R : REX_NONE)); emitUint8(0x8d); Address address(Address::addressRIPRelative(0xdeadbeef)); emitOperand(dst & 7, address); // Overwrite the fake displacement with a label or label link buffer_.remit<uint32_t>(); static const int size = 7; emitLabel(label, size); } void Assembler::movq(Register dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (imm.isUint32()) { // Pick single byte B8 encoding if possible. If dst < 8 then we also omit // the Rex byte. emitRegisterREX(dst, REX_NONE); emitUint8(0xb8 | (dst & 7)); emitUInt32(imm.value()); } else if (imm.isInt32()) { // Sign extended C7 Cx encoding if we have a negative input. Operand operand(dst); emitOperandREX(0, operand, REX_W); emitUint8(0xc7); emitOperand(0, operand); emitImmediate(imm); } else { // Full 64 bit immediate encoding. emitRegisterREX(dst, REX_W); emitUint8(0xb8 | (dst & 7)); emitImmediate(imm); } } void Assembler::movq(Address dst, Immediate imm) { CHECK(imm.isInt32(), "this instruction only exists for 32bit immediates"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(0, dst, REX_W); emitUint8(0xC7); emitOperand(0, dst); emitImmediate(imm); } void Assembler::movsb() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0xa4); } void Assembler::movsw() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandSizeOverride(); emitUint8(0xa5); } void Assembler::movsl() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0xa5); } void Assembler::movsq() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REX_PREFIX | REX_W); emitUint8(0xa5); } void Assembler::repMovsb() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REP); emitUint8(0xa4); } void Assembler::repMovsw() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REP); emitOperandSizeOverride(); emitUint8(0xa5); } void Assembler::repMovsl() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REP); emitUint8(0xa5); } void Assembler::repMovsq() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REP); emitUint8(REX_PREFIX | REX_W); emitUint8(0xa5); } void Assembler::repnzMovsb() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REPNZ); emitUint8(0xa4); } void Assembler::repnzMovsw() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REPNZ); emitOperandSizeOverride(); emitUint8(0xa5); } void Assembler::repnzMovsl() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REPNZ); emitUint8(0xa5); } void Assembler::repnzMovsq() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(REPNZ); emitUint8(REX_PREFIX | REX_W); emitUint8(0xa5); } void Assembler::emitSimple(int opcode, int opcode2) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(opcode); if (opcode2 != -1) { emitUint8(opcode2); } } void Assembler::emitQ(int dst, int src, int opcode, int prefix2, int prefix1) { DCHECK(src <= XMM15, "assert()"); DCHECK(dst <= XMM15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitRegRegREX(dst, src, REX_W); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitRegisterOperand(dst & 7, src); } void Assembler::emitL(int dst, int src, int opcode, int prefix2, int prefix1) { DCHECK(src <= XMM15, "assert()"); DCHECK(dst <= XMM15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitRegRegREX(dst, src); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitRegisterOperand(dst & 7, src); } void Assembler::emitW(Register dst, Register src, int opcode, int prefix2, int prefix1) { DCHECK(src <= R15, "assert()"); DCHECK(dst <= R15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (prefix1 >= 0) { emitUint8(prefix1); } emitOperandSizeOverride(); emitRegRegREX(dst, src); if (prefix2 >= 0) { emitUint8(prefix2); } emitUint8(opcode); emitRegisterOperand(dst & 7, src); } void Assembler::cmpPS(XmmRegister dst, XmmRegister src, int condition) { emitL(dst, src, 0xc2, 0x0f); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(condition); } void Assembler::set1ps(XmmRegister dst, Register tmp, Immediate imm) { // load 32-bit immediate value into tmp1. movl(tmp, imm); // Move value from tmp1 into dst. movd(dst, tmp); // Broadcast low lane into other three lanes. shufps(dst, dst, Immediate(0x0)); } void Assembler::shufps(XmmRegister dst, XmmRegister src, Immediate mask) { emitL(dst, src, 0xc6, 0x0f); AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(mask.isUint8(), "assert()"); emitUint8(mask.value()); } void Assembler::shufpd(XmmRegister dst, XmmRegister src, Immediate mask) { emitL(dst, src, 0xc6, 0x0f, 0x66); AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(mask.isUint8(), "assert()"); emitUint8(mask.value()); } void Assembler::roundsd(XmmRegister dst, XmmRegister src, RoundingMode mode) { DCHECK(src <= XMM15, "assert()"); DCHECK(dst <= XMM15, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0x66); emitRegRegREX(dst, src); emitUint8(0x0f); emitUint8(0x3a); emitUint8(0x0b); emitRegisterOperand(dst & 7, src); // Mask precision exeption. emitUint8(static_cast<uint8_t>(mode) | 0x8); } void Assembler::fldl(Address src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0xdd); emitOperand(0, src); } void Assembler::fstpl(Address dst) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0xdd); emitOperand(3, dst); } void Assembler::ffree(word value) { DCHECK(value < 7, "assert()"); emitSimple(0xdd, 0xc0 + value); } void Assembler::emitTestB(Operand operand, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (operand.hasRegister(RAX)) { emitUint8(0xa8); } else { emitOperandREX(0, operand, byteOperandREX(operand)); emitUint8(0xf6); emitOperand(0, operand); } DCHECK(imm.isInt8(), "immediate too large"); emitUint8(imm.value()); } void Assembler::testb(Register dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegRegREX(dst, src, byteRegisterREX(dst) | byteRegisterREX(src)); emitUint8(0x84); emitRegisterOperand(dst & 7, src); } void Assembler::testb(Register reg, Immediate imm) { emitTestB(Operand(reg), imm); } void Assembler::testb(Address address, Immediate imm) { emitTestB(address, imm); } void Assembler::testb(Address address, Register reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(reg, address, byteRegisterREX(reg)); emitUint8(0x84); emitOperand(reg & 7, address); } void Assembler::emitTestQ(Operand operand, Immediate imm) { // Try to emit a small instruction if the value of the immediate lets us. For // Address operands, this relies on the fact that x86 is little-endian. if (imm.isUint8()) { emitTestB(operand, Immediate(static_cast<int8_t>(imm.value()))); } else if (imm.isUint32()) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (operand.hasRegister(RAX)) { emitUint8(0xa9); } else { emitOperandREX(0, operand, REX_NONE); emitUint8(0xf7); emitOperand(0, operand); } emitUInt32(imm.value()); } else { AssemblerBuffer::EnsureCapacity ensured(&buffer_); // Sign extended version of 32 bit test. DCHECK(imm.isInt32(), "assert()"); emitOperandREX(0, operand, REX_W); if (operand.hasRegister(RAX)) { emitUint8(0xa9); } else { emitUint8(0xf7); emitOperand(0, operand); } emitInt32(imm.value()); } } void Assembler::testq(Register reg, Immediate imm) { emitTestQ(Operand(reg), imm); } void Assembler::testq(Address address, Immediate imm) { emitTestQ(address, imm); } void Assembler::aluB(uint8_t modrm_opcode, Register dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand dst_operand(dst); emitOperandREX(modrm_opcode, dst_operand, byteRegisterREX(dst)); emitComplexB(modrm_opcode, dst_operand, imm); } void Assembler::aluL(uint8_t modrm_opcode, Register dst, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(dst, REX_NONE); emitComplex(modrm_opcode, Operand(dst), imm); } void Assembler::aluB(uint8_t modrm_opcode, Address dst, Immediate imm) { DCHECK(imm.isUint8() || imm.isInt8(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(modrm_opcode, dst, REX_NONE); emitUint8(0x80); emitOperand(modrm_opcode, dst); emitUint8(imm.value() & 0xff); } void Assembler::aluW(uint8_t modrm_opcode, Address dst, Immediate imm) { DCHECK(imm.isInt16() || imm.isUint16(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandSizeOverride(); emitOperandREX(modrm_opcode, dst, REX_NONE); if (imm.isInt8()) { emitSignExtendedInt8(modrm_opcode, dst, imm); } else { emitUint8(0x81); emitOperand(modrm_opcode, dst); emitUint8(imm.value() & 0xff); emitUint8((imm.value() >> 8) & 0xff); } } void Assembler::aluL(uint8_t modrm_opcode, Address dst, Immediate imm) { DCHECK(imm.isInt32(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(modrm_opcode, dst, REX_NONE); emitComplex(modrm_opcode, dst, imm); } void Assembler::aluQ(uint8_t modrm_opcode, uint8_t /*opcode*/, Register dst, Immediate imm) { Operand operand(dst); if (modrm_opcode == 4 && imm.isUint32()) { // We can use andl for andq. AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(dst, REX_NONE); // Would like to use emitComplex here, but it doesn't like uint32 // immediates. if (imm.isInt8()) { emitSignExtendedInt8(modrm_opcode, operand, imm); } else { if (dst == RAX) { emitUint8(0x25); } else { emitUint8(0x81); emitOperand(modrm_opcode, operand); } emitUInt32(imm.value()); } } else { DCHECK(imm.isInt32(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(dst, REX_W); emitComplex(modrm_opcode, operand, imm); } } void Assembler::aluQ(uint8_t modrm_opcode, uint8_t /*opcode*/, Address dst, Immediate imm) { DCHECK(imm.isInt32(), "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitOperandREX(modrm_opcode, dst, REX_W); emitComplex(modrm_opcode, dst, imm); } void Assembler::cqo() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(RAX, REX_W); emitUint8(0x99); } void Assembler::emitUnaryQ(Register reg, int opcode, int modrm_code) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(reg, REX_W); emitUint8(opcode); emitOperand(modrm_code, Operand(reg)); } void Assembler::emitUnaryL(Register reg, int opcode, int modrm_code) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitRegisterREX(reg, REX_NONE); emitUint8(opcode); emitOperand(modrm_code, Operand(reg)); } void Assembler::emitUnaryQ(Address address, int opcode, int modrm_code) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(address); emitOperandREX(modrm_code, operand, REX_W); emitUint8(opcode); emitOperand(modrm_code, operand); } void Assembler::emitUnaryL(Address address, int opcode, int modrm_code) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(address); emitOperandREX(modrm_code, operand, REX_NONE); emitUint8(opcode); emitOperand(modrm_code, operand); } void Assembler::imull(Register reg, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(reg); emitOperandREX(reg, operand, REX_NONE); emitUint8(0x69); emitOperand(reg & 7, Operand(reg)); emitImmediate(imm); } void Assembler::shll(Register reg, Immediate imm) { emitGenericShift(false, 4, reg, imm); } void Assembler::shll(Register operand, Register shifter) { emitGenericShift(false, 4, operand, shifter); } void Assembler::shrl(Register reg, Immediate imm) { emitGenericShift(false, 5, reg, imm); } void Assembler::shrl(Register operand, Register shifter) { emitGenericShift(false, 5, operand, shifter); } void Assembler::sarl(Register reg, Immediate imm) { emitGenericShift(false, 7, reg, imm); } void Assembler::sarl(Register operand, Register shifter) { emitGenericShift(false, 7, operand, shifter); } void Assembler::shldl(Register dst, Register src, Immediate imm) { emitL(src, dst, 0xa4, 0x0f); AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(imm.isInt8(), "assert()"); emitUint8(imm.value() & 0xff); } void Assembler::shlq(Register reg, Immediate imm) { emitGenericShift(true, 4, reg, imm); } void Assembler::shlq(Register operand, Register shifter) { emitGenericShift(true, 4, operand, shifter); } void Assembler::shrq(Register reg, Immediate imm) { emitGenericShift(true, 5, reg, imm); } void Assembler::shrq(Register operand, Register shifter) { emitGenericShift(true, 5, operand, shifter); } void Assembler::sarq(Register reg, Immediate imm) { emitGenericShift(true, 7, reg, imm); } void Assembler::sarq(Register operand, Register shifter) { emitGenericShift(true, 7, operand, shifter); } void Assembler::shldq(Register dst, Register src, Immediate imm) { emitQ(src, dst, 0xa4, 0x0f); AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(imm.isInt8(), "assert()"); emitUint8(imm.value() & 0xff); } void Assembler::btq(Register base, int bit) { DCHECK(bit >= 0 && bit < 64, "assert()"); AssemblerBuffer::EnsureCapacity ensured(&buffer_); Operand operand(base); emitOperandREX(4, operand, bit >= 32 ? REX_W : REX_NONE); emitUint8(0x0f); emitUint8(0xba); emitOperand(4, operand); emitUint8(bit); } void Assembler::enter(Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0xc8); DCHECK(imm.isUint16(), "assert()"); emitUint8(imm.value() & 0xff); emitUint8((imm.value() >> 8) & 0xff); emitUint8(0x00); } void Assembler::nop(int size) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); // There are nops up to size 15, but for now just provide up to size 8. DCHECK(0 < size && size <= kMaxNopSize, "assert()"); switch (size) { case 1: emitUint8(0x90); break; case 2: emitUint8(0x66); emitUint8(0x90); break; case 3: emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x00); break; case 4: emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x40); emitUint8(0x00); break; case 5: emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x44); emitUint8(0x00); emitUint8(0x00); break; case 6: emitUint8(0x66); emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x44); emitUint8(0x00); emitUint8(0x00); break; case 7: emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x80); emitUint8(0x00); emitUint8(0x00); emitUint8(0x00); emitUint8(0x00); break; case 8: emitUint8(0x0f); emitUint8(0x1f); emitUint8(0x84); emitUint8(0x00); emitUint8(0x00); emitUint8(0x00); emitUint8(0x00); emitUint8(0x00); break; default: UNIMPLEMENTED("default"); } } void Assembler::nops(int size) { DCHECK(size >= 0, "Can't emit negative nops"); if (size == 0) return; while (size > kMaxNopSize) { nop(kMaxNopSize); size -= kMaxNopSize; } nop(size); } void Assembler::ud2() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); emitUint8(0x0f); emitUint8(0x0b); } void Assembler::jcc(Condition condition, Label* label, bool near) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (label->isBound()) { static const int short_size = 2; static const int long_size = 6; word offset = label->position() - buffer_.size(); DCHECK(offset <= 0, "assert()"); if (Utils::fits<int8_t>(offset - short_size)) { emitUint8(0x70 + condition); emitUint8((offset - short_size) & 0xff); } else { emitUint8(0x0f); emitUint8(0x80 + condition); emitInt32(offset - long_size); } } else if (near) { emitUint8(0x70 + condition); emitNearLabelLink(label); } else { emitUint8(0x0f); emitUint8(0x80 + condition); emitLabelLink(label); } } void Assembler::jmp(Label* label, bool near) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); if (label->isBound()) { static const int short_size = 2; static const int long_size = 5; word offset = label->position() - buffer_.size(); DCHECK(offset <= 0, "assert()"); if (Utils::fits<int8_t>(offset - short_size)) { emitUint8(0xeb); emitUint8((offset - short_size) & 0xff); } else { emitUint8(0xe9); emitInt32(offset - long_size); } } else if (near) { emitUint8(0xeb); emitNearLabelLink(label); } else { emitUint8(0xe9); emitLabelLink(label); } } void Assembler::bind(Label* label) { word bound = buffer_.size(); DCHECK(!label->isBound(), "assert()"); // Labels can only be bound once. while (label->isLinked()) { word position = label->linkPosition(); word next = buffer_.load<int32_t>(position); buffer_.store<int32_t>(position, bound - (position + 4)); label->position_ = next; } while (label->hasNear()) { word position = label->nearPosition(); word offset = bound - (position + 1); DCHECK(Utils::fits<int8_t>(offset), "assert()"); buffer_.store<int8_t>(position, offset); } label->bindTo(bound); } void Assembler::comment(const char* format, ...) { char comment_buffer[1024]; va_list args; ::va_start(args, format); std::vsnprintf(comment_buffer, sizeof comment_buffer, format, args); ::va_end(args); comments_.add(new CodeComment(buffer_.getPosition(), comment_buffer)); } void Assembler::align(int alignment) { DCHECK(Utils::isPowerOfTwo(alignment), "assert()"); word pos = buffer_.getPosition(); int mod = pos & (alignment - 1); if (mod == 0) { return; } word bytes_needed = alignment - mod; while (bytes_needed > kMaxNopSize) { nop(kMaxNopSize); bytes_needed -= kMaxNopSize; } if (bytes_needed) { nop(bytes_needed); } DCHECK((buffer_.getPosition() & (alignment - 1)) == 0, "assert()"); } void Assembler::emitOperand(int rm, Operand operand) { DCHECK(rm >= 0 && rm < 8, "assert()"); const word length = operand.length_; DCHECK(length > 0, "assert()"); // emit the ModRM byte updated with the given RM value. DCHECK((operand.encoding_[0] & 0x38) == 0, "assert()"); emitUint8(operand.encoding_[0] + (rm << 3)); // emit the rest of the encoded operand. for (word i = 1; i < length; i++) { emitUint8(operand.encoding_[i]); } } void Assembler::emitRegisterOperand(int rm, int reg) { Operand operand; operand.setModRM(3, static_cast<Register>(reg)); emitOperand(rm, operand); } void Assembler::emitImmediate(Immediate imm) { if (imm.isInt32()) { emitInt32(static_cast<int32_t>(imm.value())); } else { emitInt64(imm.value()); } } void Assembler::emitSignExtendedInt8(int rm, Operand operand, Immediate immediate) { emitUint8(0x83); emitOperand(rm, operand); emitUint8(immediate.value() & 0xff); } void Assembler::emitComplexB(int rm, Operand operand, Immediate imm) { DCHECK(rm >= 0 && rm < 8, "assert()"); DCHECK(imm.isUint8() || imm.isInt8(), "immediate too large"); if (operand.hasRegister(RAX)) { // Use short form if the destination is al. emitUint8(0x04 + (rm << 3)); } else { emitUint8(0x80); emitOperand(rm, operand); } emitUint8(imm.value()); } void Assembler::emitComplex(int rm, Operand operand, Immediate immediate) { DCHECK(rm >= 0 && rm < 8, "assert()"); DCHECK(immediate.isInt32(), "assert()"); if (immediate.isInt8()) { emitSignExtendedInt8(rm, operand, immediate); } else if (operand.hasRegister(RAX)) { // Use short form if the destination is rax. emitUint8(0x05 + (rm << 3)); emitImmediate(immediate); } else { emitUint8(0x81); emitOperand(rm, operand); emitImmediate(immediate); } } void Assembler::emitLabel(Label* label, word instruction_size) { if (label->isBound()) { word offset = label->position() - buffer_.size(); DCHECK(offset <= 0, "assert()"); emitInt32(offset - instruction_size); } else { emitLabelLink(label); } } void Assembler::emitLabelLink(Label* label) { DCHECK(!label->isBound(), "assert()"); word position = buffer_.size(); emitInt32(label->position_); label->linkTo(position); } void Assembler::emitNearLabelLink(Label* label) { DCHECK(!label->isBound(), "assert()"); word position = buffer_.size(); emitUint8(0); label->nearLinkTo(position); } void Assembler::emitGenericShift(bool wide, int rm, Register reg, Immediate imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(imm.isInt8(), "assert()"); if (wide) { emitRegisterREX(reg, REX_W); } else { emitRegisterREX(reg, REX_NONE); } if (imm.value() == 1) { emitUint8(0xd1); emitOperand(rm, Operand(reg)); } else { emitUint8(0xc1); emitOperand(rm, Operand(reg)); emitUint8(imm.value() & 0xff); } } void Assembler::emitGenericShift(bool wide, int rm, Register operand, Register shifter) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); DCHECK(shifter == RCX, "assert()"); emitRegisterREX(operand, wide ? REX_W : REX_NONE); emitUint8(0xd3); emitOperand(rm, Operand(operand)); } } // namespace x64 } // namespace py