* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/compiler/assembler/x64/assembler_x64.h"
namespace panda::ecmascript::x64 {
void AssemblerX64::Pushq(Register x)
{
EmitRexPrefix(x);
EmitU8(0x50 | x.LowBits());
}
void AssemblerX64::Pushq(Immediate x)
{
if (InRange8(x.Value())) {
EmitU8(0x6A);
EmitI8(static_cast<int8_t>(x.Value()));
} else {
EmitU8(0x68);
EmitI32(x.Value());
}
}
void AssemblerX64::Push(Register x)
{
Pushq(x);
}
void AssemblerX64::Popq(Register x)
{
EmitRexPrefix(x);
EmitU8(0x58 | x.LowBits());
}
void AssemblerX64::Pop(Register x)
{
Popq(x);
}
void AssemblerX64::Addq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(0, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x5);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(0, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Addq(Register src, Register dst)
{
EmitRexPrefix(dst, src);
EmitU8(0x03);
EmitModrm(dst, src);
}
void AssemblerX64::Addl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(0, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x5);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(0, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Subq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(5, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x2D);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(5, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Subq(Register src, Register dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x29);
EmitModrm(src, dst);
}
void AssemblerX64::Subl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(5, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x2D);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(5, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Cmpq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(7, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x3D);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(7, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Cmpb(Immediate src, Register dst)
{
EmitRexPrefix(dst);
if (InRange8(src.Value())) {
EmitU8(0x80);
EmitModrm(7, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x3C);
EmitI8(src.Value());
} else {
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
}
void AssemblerX64::Cmpq(Register src, Register dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x39);
EmitModrm(src, dst);
}
void AssemblerX64::Cmpl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(7, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x3D);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(7, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Cmp(Immediate src, Register dst)
{
Cmpq(src, dst);
}
void AssemblerX64::Movq(Register src, Register dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x89);
EmitModrm(src, dst);
}
void AssemblerX64::Mov(Register src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x8B);
EmitModrm(dst, src);
}
void AssemblerX64::Align16()
{
auto pos = GetCurrentPosition();
size_t x = 16;
size_t delta = static_cast<size_t>(x - (pos & (x - 1)));
for (size_t i = 0; i < delta; i++) {
EmitU8(0x90);
}
}
void AssemblerX64::Movq(const Operand &src, Register dst)
{
EmitRexPrefix(dst, src);
EmitU8(0x8B);
EmitOperand(dst, src);
}
void AssemblerX64::Movq(Register src, const Operand &dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x89);
EmitOperand(src, dst);
}
void AssemblerX64::Movq(Immediate src, Operand dst)
{
EmitRexPrefix(dst);
EmitU8(0xC7);
EmitOperand(0, dst);
EmitI32(src.Value());
}
void AssemblerX64::Movq(Immediate src, Register dst)
{
EmitRexPrefix(dst);
EmitU8(0xB8 | dst.LowBits());
EmitI32(src.Value());
}
void AssemblerX64::Mov(const Operand &src, Register dst)
{
Movq(src, dst);
}
void AssemblerX64::EmitJmp(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0xEB);
EmitI8(offset - SIZE_OF_INT8);
} else {
EmitU8(0xE9);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJa(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0x77);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x87);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJb(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0x72);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x82);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJz(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0x74);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x84);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJne(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0x75);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x85);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJbe(int32_t offset)
{
offset--;
if (InRange8(offset - SIZE_OF_INT8)) {
EmitU8(0x76);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x86);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJnz(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x75);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x85);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJle(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x7E);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x8E);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJae(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x73);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x83);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJg(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x7F);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x8F);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJge(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x7D);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x8D);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitJe(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x74);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x84);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::EmitCall(int32_t offset)
{
offset--;
EmitU8(0xE8);
EmitI32(offset - SIZE_OF_INT32);
}
void AssemblerX64::EmitJnb(int32_t offset)
{
offset--;
if (InRange8(offset)) {
EmitU8(0x73);
EmitI8(offset - SIZE_OF_INT8);
} else {
offset--;
EmitU8(0x0F);
EmitU8(0x83);
EmitI32(offset - SIZE_OF_INT32);
}
}
void AssemblerX64::Callq(Register addr)
{
EmitRexPrefix(addr);
EmitU8(0xFF);
EmitModrm(0x2, addr);
}
void AssemblerX64::Callq(Label *target)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitCall(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 1);
EmitU8(0xE8);
EmitI32(emitPos);
}
void AssemblerX64::Ret()
{
EmitU8(0xC3);
}
void AssemblerX64::Jmp(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJmp(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0xEB);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 1);
EmitU8(0xE9);
EmitI32(emitPos);
}
}
void AssemblerX64::Jmp(Register dst)
{
EmitRexPrefix(dst);
EmitU8(0xFF);
EmitModrm(4, dst);
}
void AssemblerX64::Jmp(Immediate offset)
{
if (InRange8(offset.Value())) {
EmitU8(0xEB);
EmitI8(static_cast<int8_t>(offset.Value()));
} else {
EmitU8(0xE9);
EmitI32(offset.Value());
}
}
void AssemblerX64::Ja(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJa(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x77);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0X0F);
EmitU8(0x87);
EmitI32(emitPos);
}
}
void AssemblerX64::Jb(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJb(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x72);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0X0F);
EmitU8(0x82);
EmitI32(emitPos);
}
}
void AssemblerX64::Jz(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJz(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x74);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0X0F);
EmitU8(0x84);
EmitI32(emitPos);
}
}
void AssemblerX64::Je(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJe(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x74);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0X0F);
EmitU8(0x84);
EmitI32(emitPos);
}
}
void AssemblerX64::Bind(Label *target)
{
size_t pos = GetCurrentPosition();
ASSERT(!target->IsBound());
if (target->IsLinked()) {
uint32_t linkPos = target->GetLinkedPos();
while (linkPos != 0) {
uint32_t preLinkPos = GetU32(linkPos);
int32_t disp = static_cast<int32_t>(pos - linkPos - sizeof(int32_t));
PutI32(linkPos, disp);
linkPos = preLinkPos;
}
}
if (target->IsLinkedNear()) {
uint32_t linkPos = target->GetLinkedNearPos();
while (linkPos != 0) {
int8_t offsetToNext = GetI8(static_cast<size_t>(linkPos));
int32_t disp = static_cast<int32_t>(pos - linkPos - sizeof(int8_t));
ASSERT(InRange8(disp));
PutI8(linkPos, static_cast<int8_t>(disp));
if (offsetToNext == 0) {
break;
}
linkPos += static_cast<uint32_t>(offsetToNext);
}
target->UnlinkNearPos();
}
target->BindTo(pos);
}
Operand::Operand(Register base, int32_t disp)
{
if (base == rsp || base == r12) {
BuildSIB(Times1, rsp, base);
}
if (disp == 0 && base != rbp && base != r13) {
BuildModerm(0, base);
} else if (AssemblerX64::InRange8(disp)) {
BuildModerm(1, base);
BuildDisp8(disp);
} else {
BuildModerm(2, base);
BuildDisp32(disp);
}
}
Operand::Operand(Register base, Register index, Scale scale, int32_t disp)
{
BuildSIB(scale, index, base);
if (disp == 0 && base != rbp && base != r13) {
BuildModerm(0, rsp);
} else if (AssemblerX64::InRange8(disp)) {
BuildModerm(1, rsp);
BuildDisp8(disp);
} else {
BuildModerm(2, rsp);
BuildDisp32(disp);
}
}
Operand::Operand(Register index, Scale scale, int32_t disp)
{
ASSERT(index != rsp);
BuildModerm(0, rsp);
BuildSIB(scale, index, rbp);
BuildDisp32(disp);
}
void Operand::BuildSIB(Scale scale, Register index, Register base)
{
sib_ = AssemblerX64::GetSIB(scale, index, base);
rex_ |= AssemblerX64::GetSIBRex(index, base);
hasSIB_ = true;
}
void Operand::BuildModerm(int32_t mode, Register rm)
{
rex_ |= AssemblerX64::GetModrmRex(rm);
moderm_ = AssemblerX64::GetModrm(mode, rm);
}
void Operand::BuildDisp8(int32_t disp)
{
disp_ = disp;
hasDisp8_ = true;
}
void Operand::BuildDisp32(int32_t disp)
{
disp_ = disp;
hasDisp32_ = true;
}
void AssemblerX64::EmitOperand(int32_t reg, Operand rm)
{
EmitU8(rm.moderm_ | (static_cast<uint32_t>(reg) << LOW_BITS_SIZE));
if (rm.hasSIB_) {
EmitU8(rm.sib_);
}
if (rm.hasDisp8_) {
EmitI8(static_cast<int8_t>(rm.disp_));
} else if (rm.hasDisp32_) {
EmitI32(rm.disp_);
}
}
void AssemblerX64::Movl(Register src, Register dst)
{
EmitRexPrefixl(src, dst);
EmitU8(0x89);
EmitModrm(src, dst);
}
void AssemblerX64::Movl(const Operand &src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x8B);
EmitOperand(dst, src);
}
void AssemblerX64::Movl(Register src, const Operand& dst)
{
EmitRexPrefixl(src, dst);
EmitU8(0x89);
EmitOperand(src, dst);
}
void AssemblerX64::Testq(Immediate src, Register dst)
{
if (InRange8(src.Value())) {
Testb(src, dst);
} else if (dst == rax) {
EmitU8(0xA9);
EmitI32(src.Value());
} else {
EmitRexPrefixW(dst);
EmitU8(0xF7);
EmitModrm(0, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Testb(Immediate src, Register dst)
{
ASSERT(InRange8(src.Value()));
if (dst == rax) {
EmitU8(0xA8);
} else {
ASSERT(dst.IsValid());
if (dst.Code() >= rsp.Code()) {
EmitRexPrefixL(dst);
}
EmitU8(0xF6);
EmitModrm(0, dst);
}
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Jne(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJne(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x75);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x85);
EmitI32(emitPos);
}
}
void AssemblerX64::Cmpl(Register src, Register dst)
{
EmitRexPrefixl(src, dst);
EmitU8(0x39);
EmitModrm(src, dst);
}
void AssemblerX64::Jbe(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJbe(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x76);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x86);
EmitI32(emitPos);
}
}
void AssemblerX64::CMovbe(Register src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x0F);
EmitU8(0x46);
EmitModrm(dst, src);
}
void AssemblerX64::Leaq(const Operand &src, Register dst)
{
EmitRexPrefix(dst, src);
EmitU8(0x8D);
EmitOperand(dst, src);
}
void AssemblerX64::Leal(const Operand &src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x8D);
EmitOperand(dst, src);
}
void AssemblerX64::Shrq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
EmitU8(0xc1);
EmitModrm(5, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Shrl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
EmitU8(0xc1);
EmitModrm(5, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Shr(Immediate src, Register dst)
{
Shrq(src, dst);
}
void AssemblerX64::Andq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x25);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(4, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Andl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x25);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(4, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::And(Register src, Register dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x21);
EmitModrm(src, dst);
}
void AssemblerX64::Or(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
if (InRange8(src.Value())) {
EmitU8(0x83);
EmitModrm(1, dst);
EmitI8(static_cast<int8_t>(src.Value()));
} else if (dst == rax) {
EmitU8(0x0D);
EmitI32(src.Value());
} else {
EmitU8(0x81);
EmitModrm(1, dst);
EmitI32(src.Value());
}
}
void AssemblerX64::Orq(Register src, Register dst)
{
EmitRexPrefix(src, dst);
EmitU8(0x09);
EmitModrm(src, dst);
}
void AssemblerX64::Jnz(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJnz(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x75);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x85);
EmitI32(emitPos);
}
}
void AssemblerX64::Jle(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJle(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x7E);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x8E);
EmitI32(emitPos);
}
}
void AssemblerX64::Jae(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJae(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x73);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x83);
EmitI32(emitPos);
}
}
void AssemblerX64::Jg(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJg(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x7F);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x8F);
EmitI32(emitPos);
}
}
void AssemblerX64::Jge(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJge(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x7D);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x8D);
EmitI32(emitPos);
}
}
void AssemblerX64::Movzbq(const Operand &src, Register dst)
{
EmitRexPrefix(dst, src);
EmitU8(0x0F);
EmitU8(0xB6);
EmitOperand(dst, src);
}
void AssemblerX64::Movzbl(const Operand &src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x0F);
EmitU8(0xB6);
EmitOperand(dst, src);
}
void AssemblerX64::Movzbl(Register src, Register dst)
{
EmitRexPrefixl(dst, src);
EmitU8(0x0F);
EmitU8(0xB6);
EmitModrm(dst, src);
}
void AssemblerX64::Btq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
EmitU8(0x0F);
EmitU8(0xBA);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Btl(Immediate src, Register dst)
{
EmitRexPrefix(dst);
EmitU8(0x0F);
EmitU8(0xBA);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Movabs(uint64_t src, Register dst)
{
EmitRexPrefixW(dst);
EmitU8(0xB8 | dst.LowBits());
EmitU64(src);
}
void AssemblerX64::Shll(Immediate src, Register dst)
{
EmitRexPrefix(dst);
EmitU8(0xC1);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Shlq(Immediate src, Register dst)
{
EmitRexPrefixW(dst);
EmitU8(0xC1);
EmitModrm(4, dst);
EmitI8(static_cast<int8_t>(src.Value()));
}
void AssemblerX64::Btsl(Register src, Register dst)
{
EmitRexPrefixl(src, dst);
EmitU8(0x0F);
EmitU8(0xAB);
EmitModrm(src, dst);
}
void AssemblerX64::Int3()
{
EmitU8(0xCC);
}
void AssemblerX64::Movzwq(const Operand &src, Register dst)
{
EmitRexPrefix(dst, src);
EmitU8(0x0F);
EmitU8(0xB7);
EmitOperand(dst, src);
}
void AssemblerX64::Jnb(Label *target, Distance distance)
{
if (target->IsBound()) {
int32_t offset = static_cast<int32_t>(target->GetPos() - GetCurrentPosition());
EmitJnb(offset);
return;
}
auto pos = GetCurrentPosition();
int32_t emitPos = 0;
if (distance == Distance::Near) {
if (target->IsLinkedNear()) {
emitPos = static_cast<int32_t>(target->GetLinkedNearPos() - pos);
}
target->LinkNearPos(pos + 1);
ASSERT(InRange8(emitPos));
EmitU8(0x73);
EmitI8(static_cast<int8_t>(emitPos));
} else {
if (target->IsLinked()) {
emitPos = static_cast<int32_t>(target->GetLinkedPos());
}
target->LinkTo(pos + 2);
EmitU8(0x0F);
EmitU8(0x83);
EmitI32(emitPos);
}
}
void AssemblerX64::Movsd(XMMRegister dst, XMMRegister src)
{
EmitU8(0xF2);
if (dst.HighBit() || src.HighBit()) {
EmitU8(0x40 | (dst.HighBit() << 2) | src.HighBit());
}
EmitU8(0x0F);
EmitU8(0x10);
EmitU8(0xC0 | (dst.LowBits() << 3) | src.LowBits());
}
void AssemblerX64::Movsd(XMMRegister dst, const Operand &src)
{
EmitU8(0xF2);
if (dst.HighBit() || src.rex_) {
EmitU8(0x40 | (dst.HighBit() << 2) | src.rex_);
}
EmitU8(0x0F);
EmitU8(0x10);
EmitOperand(dst.LowBits(), src);
}
void AssemblerX64::Movq(XMMRegister dst, Register src)
{
EmitU8(0x66);
EmitU8(0x48 | (dst.HighBit() << 2) | src.HighBit());
EmitU8(0x0F);
EmitU8(0x6E);
EmitU8(0xC0 | (dst.LowBits() << 3) | src.LowBits());
}
}