blob: 1f4d727abb09576d63cfb7f92fab95d7e888ed86 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* 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 "assembler_x86_64.h"
#include "utils/assembler_test.h"
namespace art {
TEST(AssemblerX86_64, CreateBuffer) {
AssemblerBuffer buffer;
AssemblerBuffer::EnsureCapacity ensured(&buffer);
buffer.Emit<uint8_t>(0x42);
ASSERT_EQ(static_cast<size_t>(1), buffer.Size());
buffer.Emit<int32_t>(42);
ASSERT_EQ(static_cast<size_t>(5), buffer.Size());
}
class AssemblerX86_64Test : public AssemblerTest<x86_64::X86_64Assembler, x86_64::CpuRegister,
x86_64::Immediate> {
protected:
// Get the typically used name for this architecture, e.g., aarch64, x86-64, ...
std::string GetArchitectureString() OVERRIDE {
return "x86_64";
}
std::string GetDisassembleParameters() OVERRIDE {
return " -D -bbinary -mi386:x86-64 -Mx86-64,addr64,data32 --no-show-raw-insn";
}
void SetUpHelpers() OVERRIDE {
if (registers_.size() == 0) {
registers_.push_back(new x86_64::CpuRegister(x86_64::RAX));
registers_.push_back(new x86_64::CpuRegister(x86_64::RBX));
registers_.push_back(new x86_64::CpuRegister(x86_64::RCX));
registers_.push_back(new x86_64::CpuRegister(x86_64::RDX));
registers_.push_back(new x86_64::CpuRegister(x86_64::RBP));
registers_.push_back(new x86_64::CpuRegister(x86_64::RSP));
registers_.push_back(new x86_64::CpuRegister(x86_64::RSI));
registers_.push_back(new x86_64::CpuRegister(x86_64::RDI));
registers_.push_back(new x86_64::CpuRegister(x86_64::R8));
registers_.push_back(new x86_64::CpuRegister(x86_64::R9));
registers_.push_back(new x86_64::CpuRegister(x86_64::R10));
registers_.push_back(new x86_64::CpuRegister(x86_64::R11));
registers_.push_back(new x86_64::CpuRegister(x86_64::R12));
registers_.push_back(new x86_64::CpuRegister(x86_64::R13));
registers_.push_back(new x86_64::CpuRegister(x86_64::R14));
registers_.push_back(new x86_64::CpuRegister(x86_64::R15));
}
}
std::vector<x86_64::CpuRegister*> GetRegisters() OVERRIDE {
return registers_;
}
x86_64::Immediate* CreateImmediate(int64_t imm_value) OVERRIDE {
return new x86_64::Immediate(imm_value);
}
private:
std::vector<x86_64::CpuRegister*> registers_;
};
TEST_F(AssemblerX86_64Test, Toolchain) {
EXPECT_TRUE(CheckTools());
}
TEST_F(AssemblerX86_64Test, PushqRegs) {
DriverStr(RepeatR(&x86_64::X86_64Assembler::pushq, "pushq %{reg}"), "pushq");
}
TEST_F(AssemblerX86_64Test, PushqImm) {
DriverStr(RepeatI(&x86_64::X86_64Assembler::pushq, 4U, "pushq ${imm}"), "pushqi");
}
TEST_F(AssemblerX86_64Test, MovqRegs) {
DriverStr(RepeatRR(&x86_64::X86_64Assembler::movq, "movq %{reg2}, %{reg1}"), "movq");
}
TEST_F(AssemblerX86_64Test, MovqImm) {
DriverStr(RepeatRI(&x86_64::X86_64Assembler::movq, 8U, "movq ${imm}, %{reg}"), "movqi");
}
TEST_F(AssemblerX86_64Test, AddqRegs) {
DriverStr(RepeatRR(&x86_64::X86_64Assembler::addq, "addq %{reg2}, %{reg1}"), "addq");
}
TEST_F(AssemblerX86_64Test, AddqImm) {
DriverStr(RepeatRI(&x86_64::X86_64Assembler::addq, 4U, "addq ${imm}, %{reg}"), "addqi");
}
TEST_F(AssemblerX86_64Test, SubqRegs) {
DriverStr(RepeatRR(&x86_64::X86_64Assembler::subq, "subq %{reg2}, %{reg1}"), "subq");
}
TEST_F(AssemblerX86_64Test, SubqImm) {
DriverStr(RepeatRI(&x86_64::X86_64Assembler::subq, 4U, "subq ${imm}, %{reg}"), "subqi");
}
TEST_F(AssemblerX86_64Test, CmpqRegs) {
DriverStr(RepeatRR(&x86_64::X86_64Assembler::cmpq, "cmpq %{reg2}, %{reg1}"), "cmpq");
}
TEST_F(AssemblerX86_64Test, XorqImm) {
DriverStr(RepeatRI(&x86_64::X86_64Assembler::xorq, 4U, "xorq ${imm}, %{reg}"), "xorqi");
}
TEST_F(AssemblerX86_64Test, Movl) {
GetAssembler()->movl(x86_64::CpuRegister(x86_64::R8), x86_64::CpuRegister(x86_64::R11));
GetAssembler()->movl(x86_64::CpuRegister(x86_64::RAX), x86_64::CpuRegister(x86_64::R11));
const char* expected =
"movl %R11d, %R8d\n"
"movl %R11d, %EAX\n";
DriverStr(expected, "movl");
}
TEST_F(AssemblerX86_64Test, Movw) {
GetAssembler()->movw(x86_64::Address(x86_64::CpuRegister(x86_64::RAX), 0),
x86_64::CpuRegister(x86_64::R9));
const char* expected = "movw %R9w, 0(%RAX)\n";
DriverStr(expected, "movw");
}
std::string setcc_test_fn(x86_64::X86_64Assembler* assembler) {
// From Condition
/*
kOverflow = 0,
kNoOverflow = 1,
kBelow = 2,
kAboveEqual = 3,
kEqual = 4,
kNotEqual = 5,
kBelowEqual = 6,
kAbove = 7,
kSign = 8,
kNotSign = 9,
kParityEven = 10,
kParityOdd = 11,
kLess = 12,
kGreaterEqual = 13,
kLessEqual = 14,
*/
std::string suffixes[15] = { "o", "no", "b", "ae", "e", "ne", "be", "a", "s", "ns", "pe", "po",
"l", "ge", "le" };
std::vector<x86_64::CpuRegister*> registers;
registers.push_back(new x86_64::CpuRegister(x86_64::RAX));
registers.push_back(new x86_64::CpuRegister(x86_64::RBX));
registers.push_back(new x86_64::CpuRegister(x86_64::RCX));
registers.push_back(new x86_64::CpuRegister(x86_64::RDX));
registers.push_back(new x86_64::CpuRegister(x86_64::RBP));
registers.push_back(new x86_64::CpuRegister(x86_64::RSP));
registers.push_back(new x86_64::CpuRegister(x86_64::RSI));
registers.push_back(new x86_64::CpuRegister(x86_64::RDI));
registers.push_back(new x86_64::CpuRegister(x86_64::R8));
registers.push_back(new x86_64::CpuRegister(x86_64::R9));
registers.push_back(new x86_64::CpuRegister(x86_64::R10));
registers.push_back(new x86_64::CpuRegister(x86_64::R11));
registers.push_back(new x86_64::CpuRegister(x86_64::R12));
registers.push_back(new x86_64::CpuRegister(x86_64::R13));
registers.push_back(new x86_64::CpuRegister(x86_64::R14));
registers.push_back(new x86_64::CpuRegister(x86_64::R15));
std::string byte_regs[16];
byte_regs[x86_64::RAX] = "al";
byte_regs[x86_64::RBX] = "bl";
byte_regs[x86_64::RCX] = "cl";
byte_regs[x86_64::RDX] = "dl";
byte_regs[x86_64::RBP] = "bpl";
byte_regs[x86_64::RSP] = "spl";
byte_regs[x86_64::RSI] = "sil";
byte_regs[x86_64::RDI] = "dil";
byte_regs[x86_64::R8] = "r8b";
byte_regs[x86_64::R9] = "r9b";
byte_regs[x86_64::R10] = "r10b";
byte_regs[x86_64::R11] = "r11b";
byte_regs[x86_64::R12] = "r12b";
byte_regs[x86_64::R13] = "r13b";
byte_regs[x86_64::R14] = "r14b";
byte_regs[x86_64::R15] = "r15b";
std::ostringstream str;
for (auto reg : registers) {
for (size_t i = 0; i < 15; ++i) {
assembler->setcc(static_cast<x86_64::Condition>(i), *reg);
str << "set" << suffixes[i] << " %" << byte_regs[reg->AsRegister()] << "\n";
}
}
return str.str();
}
TEST_F(AssemblerX86_64Test, SetCC) {
DriverFn(&setcc_test_fn, "setcc");
}
static x86_64::X86_64ManagedRegister ManagedFromCpu(x86_64::Register r) {
return x86_64::X86_64ManagedRegister::FromCpuRegister(r);
}
static x86_64::X86_64ManagedRegister ManagedFromFpu(x86_64::FloatRegister r) {
return x86_64::X86_64ManagedRegister::FromXmmRegister(r);
}
std::string buildframe_test_fn(x86_64::X86_64Assembler* assembler) {
// TODO: more interesting spill registers / entry spills.
// Two random spill regs.
std::vector<ManagedRegister> spill_regs;
spill_regs.push_back(ManagedFromCpu(x86_64::R10));
spill_regs.push_back(ManagedFromCpu(x86_64::RSI));
// Three random entry spills.
ManagedRegisterEntrySpills entry_spills;
ManagedRegisterSpill spill(ManagedFromCpu(x86_64::RAX), 8, 0);
entry_spills.push_back(spill);
ManagedRegisterSpill spill2(ManagedFromCpu(x86_64::RBX), 8, 8);
entry_spills.push_back(spill2);
ManagedRegisterSpill spill3(ManagedFromFpu(x86_64::XMM1), 8, 16);
entry_spills.push_back(spill3);
x86_64::X86_64ManagedRegister method_reg = ManagedFromCpu(x86_64::RDI);
size_t frame_size = 10 * kStackAlignment;
assembler->BuildFrame(10 * kStackAlignment, method_reg, spill_regs, entry_spills);
// Construct assembly text counterpart.
std::ostringstream str;
// 1) Push the spill_regs.
str << "pushq %rsi\n";
str << "pushq %r10\n";
// 2) Move down the stack pointer.
ssize_t displacement = static_cast<ssize_t>(frame_size) - (spill_regs.size() * 8 + 8);
str << "subq $" << displacement << ", %rsp\n";
// 3) Store method reference.
str << "movl %edi, (%rsp)\n";
// 4) Entry spills.
str << "movq %rax, " << frame_size + 0 << "(%rsp)\n";
str << "movq %rbx, " << frame_size + 8 << "(%rsp)\n";
str << "movsd %xmm1, " << frame_size + 16 << "(%rsp)\n";
return str.str();
}
TEST_F(AssemblerX86_64Test, BuildFrame) {
DriverFn(&buildframe_test_fn, "BuildFrame");
}
std::string removeframe_test_fn(x86_64::X86_64Assembler* assembler) {
// TODO: more interesting spill registers / entry spills.
// Two random spill regs.
std::vector<ManagedRegister> spill_regs;
spill_regs.push_back(ManagedFromCpu(x86_64::R10));
spill_regs.push_back(ManagedFromCpu(x86_64::RSI));
size_t frame_size = 10 * kStackAlignment;
assembler->RemoveFrame(10 * kStackAlignment, spill_regs);
// Construct assembly text counterpart.
std::ostringstream str;
// 1) Move up the stack pointer.
ssize_t displacement = static_cast<ssize_t>(frame_size) - spill_regs.size() * 8 - 8;
str << "addq $" << displacement << ", %rsp\n";
// 2) Pop spill regs.
str << "popq %r10\n";
str << "popq %rsi\n";
str << "ret\n";
return str.str();
}
TEST_F(AssemblerX86_64Test, RemoveFrame) {
DriverFn(&removeframe_test_fn, "RemoveFrame");
}
std::string increaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
assembler->IncreaseFrameSize(0U);
assembler->IncreaseFrameSize(kStackAlignment);
assembler->IncreaseFrameSize(10 * kStackAlignment);
// Construct assembly text counterpart.
std::ostringstream str;
str << "addq $0, %rsp\n";
str << "addq $-" << kStackAlignment << ", %rsp\n";
str << "addq $-" << 10 * kStackAlignment << ", %rsp\n";
return str.str();
}
TEST_F(AssemblerX86_64Test, IncreaseFrame) {
DriverFn(&increaseframe_test_fn, "IncreaseFrame");
}
std::string decreaseframe_test_fn(x86_64::X86_64Assembler* assembler) {
assembler->DecreaseFrameSize(0U);
assembler->DecreaseFrameSize(kStackAlignment);
assembler->DecreaseFrameSize(10 * kStackAlignment);
// Construct assembly text counterpart.
std::ostringstream str;
str << "addq $0, %rsp\n";
str << "addq $" << kStackAlignment << ", %rsp\n";
str << "addq $" << 10 * kStackAlignment << ", %rsp\n";
return str.str();
}
TEST_F(AssemblerX86_64Test, DecreaseFrame) {
DriverFn(&decreaseframe_test_fn, "DecreaseFrame");
}
} // namespace art