blob: 6dc2e9bc66dc095fa7fd77475436c68e182ef506 [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 "gtest/gtest.h"
#include <sys/mman.h>
#include <cstring>
#include <iterator>
#include <string>
#include "berberis/assembler/machine_code.h"
#include "berberis/assembler/rv32e.h"
#include "berberis/assembler/rv32i.h"
#include "berberis/assembler/rv64i.h"
#include "berberis/assembler/x86_32.h"
#include "berberis/assembler/x86_64.h"
#include "berberis/base/bit_util.h"
#include "berberis/base/logging.h"
#include "berberis/test_utils/scoped_exec_region.h"
#if defined(__i386__)
using CodeEmitter = berberis::x86_32::Assembler;
#elif defined(__amd64__)
using CodeEmitter = berberis::x86_64::Assembler;
#elif defined(__riscv)
using CodeEmitter = berberis::rv64::Assembler;
#else
#error "Unsupported platform"
#endif
namespace berberis {
enum class CPUArch {
kX86_64,
kRiscv64,
};
int Callee() {
return 239;
}
float FloatFunc(float f1, float f2) {
return f1 - f2;
}
inline bool IsInstructionEqual(std::string code_str1,
std::string code_str2,
uint32_t insn,
uint32_t insn_size) {
return code_str1.compare(
insn * (insn_size + 1), insn_size, code_str2, insn * (insn_size + 1), insn_size) == 0;
}
template <typename ParcelInt>
inline bool CompareCode(const ParcelInt* code_template_begin,
const ParcelInt* code_template_end,
const MachineCode& code,
CPUArch arch) {
if ((code_template_end - code_template_begin) * sizeof(ParcelInt) != code.install_size()) {
ALOGE("Code size mismatch: %zd != %u",
(code_template_end - code_template_begin) * static_cast<unsigned>(sizeof(ParcelInt)),
code.install_size());
return false;
}
if (memcmp(code_template_begin, code.AddrAs<uint8_t>(0), code.install_size()) != 0) {
ALOGE("Code mismatch");
MachineCode code2;
code2.AddSequence(code_template_begin, code_template_end - code_template_begin);
std::string code_str1, code_str2;
uint32_t insn_size = 0;
switch (arch) {
case CPUArch::kRiscv64:
insn_size = 8;
code.AsString(&code_str1, InstructionSize::FourBytes);
code2.AsString(&code_str2, InstructionSize::FourBytes);
break;
case CPUArch::kX86_64:
insn_size = 2;
code.AsString(&code_str1, InstructionSize::OneByte);
code2.AsString(&code_str2, InstructionSize::OneByte);
break;
}
CHECK_EQ(code_str1.size() % (insn_size + 1), 0);
CHECK_EQ(code_str2.size() % (insn_size + 1), 0);
uint32_t number_of_instructions = code_str1.size() / (insn_size + 1);
// Skip identical part.
uint32_t insn = 0;
while (insn < number_of_instructions &&
IsInstructionEqual(code_str1, code_str2, insn, insn_size)) {
insn++;
}
for (uint32_t i = insn; i < insn + 20 && i < number_of_instructions; i++) {
ALOGE("Assembler generated: %s, should be %s\n",
code_str1.substr(i * (insn_size + 1), insn_size).c_str(),
code_str2.substr(i * (insn_size + 1), insn_size).c_str());
}
return false;
}
return true;
}
namespace rv32 {
bool AssemblerTest() {
MachineCode code;
Assembler assembler(&code);
Assembler::Label data_begin, data_end, label;
assembler.Bind(&data_begin);
// We test loads and stores twice to ensure that both positive and negative immediates are
// present both in auipc and in the follow-up load/store instructions.
assembler.Fld(Assembler::f1, data_end, Assembler::x2);
assembler.Flw(Assembler::f3, data_end, Assembler::x4);
assembler.Fsd(Assembler::f5, data_end, Assembler::x6);
assembler.Fsw(Assembler::f7, data_end, Assembler::x8);
assembler.Lb(Assembler::x9, data_end);
assembler.Lbu(Assembler::x10, data_end);
assembler.Lh(Assembler::x11, data_end);
assembler.Lhu(Assembler::x12, data_end);
assembler.Lw(Assembler::x13, data_end);
assembler.Sb(Assembler::x14, data_end, Assembler::x15);
assembler.Sh(Assembler::x16, data_end, Assembler::x17);
assembler.Sw(Assembler::x18, data_end, Assembler::x19);
assembler.La(Assembler::x20, data_end);
assembler.Bcc(Assembler::Condition::kEqual, Assembler::x1, Assembler::x2, label);
assembler.Bcc(Assembler::Condition::kNotEqual, Assembler::x3, Assembler::x4, label);
assembler.Bcc(Assembler::Condition::kLess, Assembler::x5, Assembler::x6, label);
assembler.Bcc(Assembler::Condition::kGreaterEqual, Assembler::x7, Assembler::x8, label);
assembler.Bcc(Assembler::Condition::kBelow, Assembler::x9, Assembler::x10, label);
assembler.Bcc(Assembler::Condition::kAboveEqual, Assembler::x11, Assembler::x12, label);
assembler.Jal(Assembler::x1, label);
assembler.Add(Assembler::x1, Assembler::x2, Assembler::x3);
assembler.Addi(Assembler::x1, Assembler::x2, 42);
assembler.Bind(&label);
// Jalr have two alternate forms.
assembler.Jalr(Assembler::x1, Assembler::x2, 42);
assembler.Jalr(Assembler::x3, {.base = Assembler::x4, .disp = 42});
assembler.Sw(Assembler::x1, {.base = Assembler::x2, .disp = 42});
assembler.Jal(Assembler::x2, label);
assembler.Beq(Assembler::x1, Assembler::x2, label);
assembler.Bne(Assembler::x3, Assembler::x4, label);
assembler.Blt(Assembler::x5, Assembler::x6, label);
assembler.Bge(Assembler::x7, Assembler::x8, label);
assembler.Bltu(Assembler::x9, Assembler::x10, label);
assembler.Bgeu(Assembler::x11, Assembler::x12, label);
assembler.Csrrc(Assembler::x1, Assembler::Csr::kVl, Assembler::x2);
assembler.Csrrs(Assembler::x3, Assembler::Csr::kVtype, Assembler::x4);
assembler.Csrrw(Assembler::x5, Assembler::Csr::kVlenb, Assembler::x6);
assembler.Slli(Assembler::x1, Assembler::x2, 3);
assembler.Srai(Assembler::x4, Assembler::x5, 6);
assembler.Srli(Assembler::x7, Assembler::x8, 9);
assembler.FcvtSW(Assembler::f1, Assembler::x2, Assembler::Rounding::kRmm);
assembler.FcvtSWu(Assembler::f3, Assembler::x4);
assembler.FcvtWS(Assembler::x1, Assembler::f2, Assembler::Rounding::kRmm);
assembler.FcvtWuS(Assembler::x3, Assembler::f4);
assembler.FsqrtS(Assembler::f1, Assembler::f2, Assembler::Rounding::kRmm);
assembler.FsqrtD(Assembler::f3, Assembler::f4);
assembler.PrefetchI({.base = Assembler::x1, .disp = 32});
assembler.PrefetchR({.base = Assembler::x2, .disp = 64});
assembler.PrefetchW({.base = Assembler::x3, .disp = 96});
assembler.Li(Assembler::x15, static_cast<int32_t>(0xaf));
assembler.Seqz(Assembler::x20, Assembler::x10);
assembler.Snez(Assembler::x2, Assembler::x9);
assembler.Sltz(Assembler::x30, Assembler::x1);
assembler.Sgtz(Assembler::x25, Assembler::x16);
assembler.J(0x42);
assembler.Jal(-0x26);
assembler.Jr(Assembler::x19);
assembler.Jalr(Assembler::x7);
// Move target position for more than 2048 bytes down to ensure auipc would use non-zero
// immediate.
for (size_t index = 138; index < 1200; ++index) {
assembler.TwoByte(uint16_t{0});
}
assembler.Fld(Assembler::f1, data_begin, Assembler::x2);
assembler.Flw(Assembler::f3, data_begin, Assembler::x4);
assembler.Fsd(Assembler::f5, data_begin, Assembler::x6);
assembler.Fsw(Assembler::f7, data_begin, Assembler::x8);
assembler.Lb(Assembler::x9, data_begin);
assembler.Lbu(Assembler::x10, data_begin);
assembler.Lh(Assembler::x11, data_begin);
assembler.Lhu(Assembler::x12, data_begin);
assembler.Lw(Assembler::x13, data_begin);
assembler.Sb(Assembler::x14, data_begin, Assembler::x15);
assembler.Sh(Assembler::x16, data_begin, Assembler::x17);
assembler.Sw(Assembler::x18, data_begin, Assembler::x19);
assembler.La(Assembler::x20, data_begin);
assembler.Bind(&data_end);
assembler.Bexti(Assembler::x16, Assembler::x1, 20);
assembler.Rori(Assembler::x5, Assembler::x3, 5);
assembler.Finalize();
// clang-format off
static const uint16_t kCodeTemplate[] = {
0x1117, 0x0000, // begin: auipc x2, 4096
0x3087, 0x9c81, // fld f1, -1592(x2)
0x1217, 0x0000, // auipc x4, 4096
0x2187, 0x9c02, // flw f3, -1600(x4)
0x1317, 0x0000, // auipc x6, 4096
0x3c27, 0x9a53, // fsd f5, -1608(x6)
0x1417, 0x0000, // auipc x8, 4096
0x2827, 0x9a74, // fsw f7, -1616(x8)
0x1497, 0x0000, // auipc x9, 4096
0x8483, 0x9a84, // lb x9, -1624(x9)
0x1517, 0x0000, // auipc x10, 4096
0x4503, 0x9a05, // lbu x10, -1632(x10)
0x1597, 0x0000, // auipc x11, 4096
0x9583, 0x9985, // lh x11, -1640(x11)
0x1617, 0x0000, // auipc x12, 4096
0x5603, 0x9906, // lhu x12, -1648(x12)
0x1697, 0x0000, // auipc x13, 4096
0xa683, 0x9886, // lw x13, -1656(x13)
0x1797, 0x0000, // auipc x15, 4096
0x8023, 0x98e7, // sb x14, -1664(x15)
0x1897, 0x0000, // auipc x17, 4096
0x9c23, 0x9708, // sh x16, -1672(x17)
0x1997, 0x0000, // auipc x19, 4096
0xa823, 0x9729, // sw x18, -1680(x19)
0x1a17, 0x0000, // auipc x20, 4096
0x0a13, 0x968a, // addi x20, x20, -1688
0x8263, 0x0220, // beq x1, x2, label
0x9063, 0x0241, // bne x3, x4, label
0xce63, 0x0062, // blt x5, x6, label
0xdc63, 0x0083, // bge x7, x8, label
0xea63, 0x00a4, // bltu x9, x10, label
0xf863, 0x00c5, // bgeu x11, x12, label
0x00ef, 0x00c0, // jal x1, label
0x00b3, 0x0031, // add x1, x2, x3
0x0093, 0x02a1, // addi x1, x2, 42
0x00e7, 0x02a1, // label: jalr x1, x2, 42
0x01e7, 0x02a2, // jalr x3, 42(x4)
0x2523, 0x0211, // sw x1, 42(x2)
0xf16f, 0xff5f, // jal x2, label
0x88e3, 0xfe20, // beq x1, x2, label
0x96e3, 0xfe41, // bne x3, x4, label
0xc4e3, 0xfe62, // blt x5, x6, label
0xd2e3, 0xfe83, // bge x7, x8, label
0xe0e3, 0xfea4, // bltu x9, x10, label
0xfee3, 0xfcc5, // bgeu x11, x12, label
0x30f3, 0xc201, // csrrc x1, vl, x2
0x21f3, 0xc212, // csrrs x3, vtype, x4
0x12f3, 0xc223, // csrrw x5, vlenb, x6
0x1093, 0x0031, // slli x1, x2, 3
0xd213, 0x4062, // srai x4, x5, 6
0x5393, 0x0094, // srli x7, x8, 9
0x40d3, 0xd001, // fcvt.s.w f1, x2, rmm
0x71d3, 0xd012, // fcvt.s.wu f3, x4
0x40d3, 0xc001, // fcvt.w.s x1, f2, rmm
0x71d3, 0xc012, // fcvt.wu.s x3, f4
0x40d3, 0x5801, // fsqrt.s f1, f2, rmm
0x71d3, 0x5a02, // fsqrt.d f3, f4
0xe013, 0x0200, // prefetch.i 32(x1)
0x6013, 0x0411, // prefetch.r 64(x2)
0xe013, 0x0631, // prefetch.w 96(x3)
0x0793, 0x0af0, // addi x15, x15, 0xaf
0x3a13, 0x0015, // sltiu x20, x10, 1
0x3133, 0x0090, // sltu x2, x0, x9
0xaf33, 0x0000, // slt x30, x1, x0
0x2cb3, 0x0100, // slt x25, x0, x16
0x006f, 0x0420, // jal zero, 0x42
0xf0ef, 0xfdbf, // jal x1, -0x26
0x8067, 0x0009, // jalr zero, x19, 0
0x80e7, 0x0003, // jalr x1, x7, 0
[ 138 ... 1199 ] = 0,// padding
0xf117, 0xffff, // auipc x2, -4096
0x3087, 0x6a01, // fld f1,1696(x2)
0xf217, 0xffff, // auipc x4, -4096
0x2187, 0x6982, // flw f3,1688(x4)
0xf317, 0xffff, // auipc x6, -4096
0x3827, 0x6853, // fsd f5,1680(x6)
0xf417, 0xffff, // auipc x8, -4096
0x2427, 0x6874, // fsw f7,1672(x8)
0xf497, 0xffff, // auipc x9, -4096
0x8483, 0x6804, // lb x9,1664(x9)
0xf517, 0xffff, // auipc x10, -4096
0x4503, 0x6785, // lbu x10,1656(x10)
0xf597, 0xffff, // auipc x11, -4096
0x9583, 0x6705, // lh x11,1648(x11)
0xf617, 0xffff, // auipc x12, -4096
0x5603, 0x6686, // lhu x12,1640(x12)
0xf697, 0xffff, // auipc x13, -4096
0xa683, 0x6606, // lw x13,1632(x13)
0xf797, 0xffff, // auipc x15, -4096
0x8c23, 0x64e7, // sb x14,1624(x15)
0xf897, 0xffff, // auipc x17, -4096
0x9823, 0x6508, // sh x16,1616(x17)
0xf997, 0xffff, // auipc x19, -4096
0xa423, 0x6529, // sw x18,1608(x19)
0xfa17, 0xffff, // auipc x20, -4096
0x0a13, 0x640a, // addi x20,x20,1600
0xd813, 0x4940, // bexti x16,x1,20
0xd293, 0x6051, // rori x5, x3, 5
}; // end:
// clang-format on
return CompareCode(std::begin(kCodeTemplate), std::end(kCodeTemplate), code, CPUArch::kRiscv64);
}
} // namespace rv32
namespace rv64 {
bool AssemblerTest() {
MachineCode code;
Assembler assembler(&code);
Assembler::Label data_begin, data_end;
assembler.Bind(&data_begin);
// We test loads and stores twice to ensure that both positive and negative immediates are
// present both in auipc and in the follow-up load/store instructions.
assembler.Ld(Assembler::x1, data_end);
assembler.Lwu(Assembler::x2, data_end);
assembler.Sd(Assembler::x3, data_end, Assembler::x4);
assembler.Bcc(Assembler::Condition::kAlways, Assembler::x1, Assembler::x2, 48);
assembler.Bcc(Assembler::Condition::kEqual, Assembler::x3, Assembler::x4, 44);
assembler.Bcc(Assembler::Condition::kNotEqual, Assembler::x5, Assembler::x6, 40);
assembler.Bcc(Assembler::Condition::kLess, Assembler::x7, Assembler::x8, 36);
assembler.Bcc(Assembler::Condition::kGreaterEqual, Assembler::x9, Assembler::x10, 32);
assembler.Bcc(Assembler::Condition::kBelow, Assembler::x11, Assembler::x12, 28);
assembler.Bcc(Assembler::Condition::kAboveEqual, Assembler::x13, Assembler::x14, 24);
assembler.Jal(Assembler::x1, 20);
assembler.Add(Assembler::x1, Assembler::x2, Assembler::x3);
assembler.Addw(Assembler::x1, Assembler::x2, Assembler::x3);
assembler.Addi(Assembler::x1, Assembler::x2, 42);
assembler.Addiw(Assembler::x1, Assembler::x2, 42);
// Jalr have two alternate forms.
assembler.Jalr(Assembler::x1, Assembler::x2, 42);
assembler.Jalr(Assembler::x3, {.base = Assembler::x4, .disp = 42});
assembler.Sw(Assembler::x1, {.base = Assembler::x2, .disp = 42});
assembler.Sd(Assembler::x3, {.base = Assembler::x4, .disp = 42});
assembler.Jal(Assembler::x2, -16);
assembler.Beq(Assembler::x1, Assembler::x2, -20);
assembler.Bne(Assembler::x3, Assembler::x4, -24);
assembler.Blt(Assembler::x5, Assembler::x6, -28);
assembler.Bge(Assembler::x7, Assembler::x8, -32);
assembler.Bltu(Assembler::x9, Assembler::x10, -36);
assembler.Bgeu(Assembler::x11, Assembler::x12, -40);
assembler.Bcc(Assembler::Condition::kAlways, Assembler::x13, Assembler::x14, -44);
assembler.Csrrc(Assembler::x1, Assembler::Csr::kVl, 2);
assembler.Csrrs(Assembler::x3, Assembler::Csr::kVtype, 4);
assembler.Csrrw(Assembler::x5, Assembler::Csr::kVlenb, 6);
assembler.Csrrci(Assembler::x7, Assembler::Csr::kVl, 8);
assembler.Csrrsi(Assembler::x9, Assembler::Csr::kVtype, 10);
assembler.Csrrwi(Assembler::x11, Assembler::Csr::kVlenb, 12);
assembler.Slliw(Assembler::x1, Assembler::x2, 3);
assembler.Sraiw(Assembler::x4, Assembler::x5, 6);
assembler.Srliw(Assembler::x7, Assembler::x8, 9);
assembler.FcvtDL(Assembler::f1, Assembler::x2, Assembler::Rounding::kRmm);
assembler.FcvtDLu(Assembler::f3, Assembler::x4);
assembler.FcvtLD(Assembler::x1, Assembler::f2, Assembler::Rounding::kRmm);
assembler.FcvtLuD(Assembler::x3, Assembler::f4);
assembler.FsqrtS(Assembler::f1, Assembler::f2, Assembler::Rounding::kRmm);
assembler.FsqrtD(Assembler::f3, Assembler::f4);
assembler.PrefetchI({.base = Assembler::x1, .disp = 32});
assembler.PrefetchR({.base = Assembler::x2, .disp = 64});
assembler.PrefetchW({.base = Assembler::x3, .disp = 96});
assembler.Li(Assembler::x10, static_cast<int64_t>(0xaaaa'0aa0'aaa0'0aaa));
assembler.Ret();
assembler.Call(data_end);
assembler.Tail(data_end);
assembler.Bgt(Assembler::x4, Assembler::x0, data_end);
assembler.Bgtu(Assembler::x2, Assembler::x20, data_end);
assembler.Ble(Assembler::x1, Assembler::x30, data_end);
assembler.Bleu(Assembler::x8, Assembler::x16, data_end);
assembler.Beqz(Assembler::x5, data_end);
assembler.Bnez(Assembler::x4, data_end);
assembler.Blez(Assembler::x2, data_end);
assembler.Bgez(Assembler::x3, data_end);
assembler.Bltz(Assembler::x9, data_end);
assembler.Bgtz(Assembler::x12, data_end);
// Move target position for more than 2048 bytes down to ensure auipc would use non-zero
// immediate.
for (size_t index = 142; index < 1200; ++index) {
assembler.TwoByte(uint16_t{0});
}
assembler.Ld(Assembler::x1, data_begin);
assembler.Lwu(Assembler::x2, data_begin);
assembler.Sd(Assembler::x3, data_begin, Assembler::x4);
assembler.Bind(&data_end);
assembler.SextW(Assembler::x15, Assembler::x12);
assembler.AddUW(Assembler::x14, Assembler::x22, Assembler::x29);
assembler.ZextW(Assembler::x13, Assembler::x21);
assembler.Sh3add(Assembler::x13, Assembler::x9, Assembler::x10);
assembler.Bexti(Assembler::x16, Assembler::x1, 53);
assembler.Rori(Assembler::x22, Assembler::x30, 43);
assembler.Roriw(Assembler::x29, Assembler::x2, 30);
assembler.Ror(Assembler::x14, Assembler::x1, Assembler::x10);
assembler.Rorw(Assembler::x25, Assembler::x5, Assembler::x4);
assembler.Not(Assembler::x10, Assembler::x4);
assembler.Neg(Assembler::x11, Assembler::x3);
assembler.Negw(Assembler::x12, Assembler::x2);
assembler.SextB(Assembler::x22, Assembler::x7);
assembler.SextH(Assembler::x23, Assembler::x8);
assembler.Finalize();
// clang-format off
static const uint16_t kCodeTemplate[] = {
0x1097, 0x0000, // begin: auipc x1, 4096
0xb083, 0x9780, // ld, x1, -1672(x1)
0x1117, 0x0000, // auipc x2, 4096
0x6103, 0x9701, // lwu x2,-1680(x2)
0x1217, 0x0000, // auipc x4, 4096
0x3423, 0x9632, // sd x3,-1688(x4)
0x006f, 0x0300, // jal x0, label
0x8663, 0x0241, // beq x1, x2, label
0x9463, 0x0262, // bne x3, x4, label
0xc263, 0x0283, // blt x5, x6, label
0xd063, 0x02a4, // bge x7, x8, label
0xee63, 0x00c5, // bltu x9, x10, label
0xfc63, 0x00e6, // bgeu x11, x12, label
0x00ef, 0x0140, // jal x1, label
0x00b3, 0x0031, // add x1, x2, x3
0x00bb, 0x0031, // addw x1, x2, x3
0x0093, 0x02a1, // addi x1, x2, 42
0x009b, 0x02a1, // addiw x1, x2, 42
0x00e7, 0x02a1, // label: jalr x1, x2, 42
0x01e7, 0x02a2, // jalr x3, 42(x4)
0x2523, 0x0211, // sw x1, 42(x2)
0x3523, 0x0232, // sd x3, 42(x4)
0xf16f, 0xff1f, // jal x2, label
0x86e3, 0xfe20, // beq x1, x2, label
0x94e3, 0xfe41, // bne x3, x4, label
0xc2e3, 0xfe62, // blt x5, x6, label
0xd0e3, 0xfe83, // bge x7, x8, label
0xeee3, 0xfca4, // bltu x9, x10, label
0xfce3, 0xfcc5, // bgeu x11, x12, label
0xf06f, 0xfd5f, // jal x0, label
0x70f3, 0xc201, // csrrc x1, vl, 2
0x61f3, 0xc212, // csrrs x3, vtype, 4
0x52f3, 0xc223, // csrrw x5, vlenb, 6
0x73f3, 0xc204, // csrrci x7, vl, 8
0x64f3, 0xc215, // csrrsi x9, vtype, 10
0x55f3, 0xc226, // csrrwi x11, vlenb, 12
0x109b, 0x0031, // slliw x1, x2, 3
0xd21b, 0x4062, // sraiw x4, x5, 6
0x539b, 0x0094, // srliw x7, x8, 9
0x40d3, 0xd221, // fcvt.d.l f1, x2, rmm
0x71d3, 0xd232, // fcvt.d.lu f3, x4
0x40d3, 0xc221, // fcvt.l.d x1, f2, rmm
0x71d3, 0xc232, // fcvt.lu.d x3, f4
0x40d3, 0x5801, // fsqrt.s f1, f2, rmm
0x71d3, 0x5a02, // fsqrt.d f3, f4
0xe013, 0x0200, // prefetch.i 32(x1)
0x6013, 0x0411, // prefetch.r 64(x2)
0xe013, 0x0631, // prefetch.w 96(x3)
0x5537,0xfd55, // lui a0, 0xfd555
0x0513, 0x0555, // addi a0, a0, 85
0x1513, 0x00d5, // slli a0, a0, 0xd
0x0513, 0x0ab5, // addi a0, a0, 171
0x1513, 0x00c5, // slli a0, a0, 0xc
0x0513, 0xa015, // addi a0, a0, -1535
0x1513, 0x00c5, // slli a0, a0, 0xc
0x0513, 0xaaa5, // addi a0,a0,-1366
0x8067, 0x0000, // ret
0x1317, 0x0000, // auipc x6, 0x1
0x00e7, 0x8943, // jalr x1, x6, -1900
0x1317, 0x0000, // auipc x6, 0x1
0x0067, 0x88c3, // jalr x0, x6, -1908
0x42e3, 0x0840, // blt x0, x4, 0x884
0x60e3, 0x082a, // bltu x20, x2, 0x880
0x5ee3, 0x061f, // bge x30, x1, 0x87c
0x7ce3, 0x0688, // bgeu x16, x8, 0x878
0x8ae3, 0x0602, // beq x5, 0x874
0x18e3, 0x0602, // bne x4, 0x870
0x56e3, 0x0620, // ble x2, 0x86c
0xd4e3, 0x0601, // bge x3, 0x868
0xc2e3, 0x0604, // blt x9, 0x864
0x40e3, 0x06c0, // bgt x12, 0x860
[ 142 ... 1199 ] = 0,// padding
0xf097, 0xffff, // auipc x1, -4096
0xb083, 0x6a00, // ld x1, 1696(x1)
0xf117, 0xffff, // auipc x2, -4096
0x6103, 0x6981, // lwu x2, 1688(x2)
0xf217, 0xffff, // auipc x4, -4096
0x3823, 0x6832, // sd x3, 1680(x4)
0x079b, 0x0006, // addi.w x15, x12, 0
0x073b, 0x09db, // add.uw x14, x22, x29
0x86bb, 0x080a, // add.uw x13, x21, zero
0xe6b3, 0x20a4, // sh3add x13, x9, x10
0xd813, 0x4b50, // bexti x16, x1, 53
0x5b13, 0x62bf, // rori x22, x30, 43
0x5e9b, 0x61e1, // roriw x29, x2, 30
0xd733, 0x60a0, // ror x14, x1, x10
0xdcbb, 0x6042, // rorw x25, x5, x4
0x4513, 0xfff2, // xori x10, x4, -1
0x05b3, 0x4030, // sub x11, zero, x3
0x063b, 0x4020, // subw x12, zero, x2
0x9b13, 0x6043, // sext.b x22, x7
0x1b93, 0x6054, // sext.h x23, x8
}; // end:
// clang-format on
return CompareCode(std::begin(kCodeTemplate), std::end(kCodeTemplate), code, CPUArch::kRiscv64);
}
} // namespace rv64
namespace x86_32 {
bool AssemblerTest() {
MachineCode code;
Assembler assembler(&code);
assembler.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
assembler.CmpXchgl({.base = Assembler::esp, .disp = 4}, Assembler::eax);
assembler.Subl(Assembler::esp, 16);
assembler.Movl({.base = Assembler::esp}, Assembler::eax);
assembler.Push(Assembler::esp);
assembler.Push(0xcccccccc);
assembler.Pushl({.base = Assembler::esp, .disp = 0x428});
assembler.Popl({.base = Assembler::esp, .disp = 0x428});
assembler.Movl(Assembler::ecx, 0xcccccccc);
assembler.Call(Assembler::ecx);
assembler.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 8});
assembler.Addl(Assembler::esp, 24);
assembler.Ret();
assembler.Finalize();
// clang-format off
static const uint8_t kCodeTemplate[] = {
0x8b, 0x44, 0x24, 0x04, // mov 0x4(%esp),%eax
0x0f, 0xb1, 0x44, 0x24, 0x04, // cmpxchg 0x4(%esp),%eax
0x83, 0xec, 0x10, // sub $16, %esp
0x89, 0x04, 0x24, // mov %eax,(%esp)
0x54, // push %esp
0x68, 0xcc, 0xcc, 0xcc, 0xcc, // push $cccccccc
0xff, 0xb4, 0x24, 0x28, 0x04, 0x00, 0x00, // pushl 0x428(%esp)
0x8f, 0x84, 0x24, 0x28, 0x04, 0x00, 0x00, // popl 0x428(%esp)
0xb9, 0xcc, 0xcc, 0xcc, 0xcc, // mov $cccccccc, %ecx
0xff, 0xd1, // call *%ecx
0x8b, 0x44, 0x24, 0x08, // mov 0x8(%esp),%eax
0x83, 0xc4, 0x18, // add $24, %esp
0xc3 // ret
};
// clang-format on
return CompareCode(std::begin(kCodeTemplate), std::end(kCodeTemplate), code, CPUArch::kX86_64);
}
} // namespace x86_32
namespace x86_64 {
bool AssemblerTest() {
MachineCode code;
Assembler assembler(&code);
assembler.Movq(Assembler::rax, Assembler::rdi);
assembler.Subq(Assembler::rsp, 16);
assembler.Movq({.base = Assembler::rsp}, Assembler::rax);
assembler.Movq({.base = Assembler::rsp, .disp = 8}, Assembler::rax);
assembler.Movl({.base = Assembler::rax, .disp = 16}, 239);
assembler.Movq(Assembler::r11, {.base = Assembler::rsp});
assembler.Addq(Assembler::rsp, 16);
assembler.Ret();
assembler.Finalize();
// clang-format off
static const uint8_t kCodeTemplate[] = {
0x48, 0x89, 0xf8, // mov %rdi, %rax
0x48, 0x83, 0xec, 0x10, // sub $0x10, %rsp
0x48, 0x89, 0x04, 0x24, // mov rax, (%rsp)
0x48, 0x89, 0x44, 0x24, 0x08, // mov rax, 8(%rsp)
0xc7, 0x40, 0x10, 0xef, 0x00, // movl $239, 0x10(%rax)
0x00, 0x00,
0x4c, 0x8b, 0x1c, 0x24, // mov (%rsp), r11
0x48, 0x83, 0xc4, 0x10, // add $0x10, %rsp
0xc3 // ret
};
// clang-format on
return CompareCode(std::begin(kCodeTemplate), std::end(kCodeTemplate), code, CPUArch::kX86_64);
}
} // namespace x86_64
#if defined(__i386__)
namespace x86_32 {
bool LabelTest() {
MachineCode code;
CodeEmitter as(&code);
Assembler::Label skip, skip2, back, end;
as.Call(bit_cast<const void*>(&Callee));
as.Jmp(skip);
as.Movl(Assembler::eax, 2);
as.Bind(&skip);
as.Addl(Assembler::eax, 8);
as.Jmp(skip2);
as.Bind(&back);
as.Addl(Assembler::eax, 12);
as.Jmp(end);
as.Bind(&skip2);
as.Jmp(back);
as.Bind(&end);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
int result = exec.get<int()>()();
return result == 239 + 8 + 12;
}
bool CondTest1() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::eax, 0xcccccccc);
as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
as.Cmpl(Assembler::edx, Assembler::ecx);
as.Setcc(Assembler::Condition::kEqual, Assembler::eax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(int, int);
auto target_func = exec.get<TestFunc>();
uint32_t result = target_func(1, 2);
if (result != 0xcccccc00) {
ALOGE("Bug in seteq(not equal): %x", result);
return false;
}
result = target_func(-1, -1);
if (result != 0xcccccc01) {
ALOGE("Bug in seteq(equal): %x", result);
return false;
}
return true;
}
bool CondTest2() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
as.Xorl(Assembler::eax, Assembler::eax);
as.Testb(Assembler::edx, Assembler::ecx);
as.Setcc(Assembler::Condition::kNotZero, Assembler::eax);
as.Xchgl(Assembler::eax, Assembler::ecx);
as.Xchgl(Assembler::ecx, Assembler::eax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(int, int);
auto target_func = exec.get<TestFunc>();
uint32_t result = target_func(0x11, 1);
if (result != 0x1) {
ALOGE("Bug in testb(not zero): %x", result);
return false;
}
result = target_func(0x11, 0x8);
if (result != 0x0) {
ALOGE("Bug in testb(zero): %x", result);
return false;
}
return true;
}
bool JccTest() {
MachineCode code;
CodeEmitter as(&code);
Assembler::Label equal, above, below, done;
as.Movl(Assembler::edx, {.base = Assembler::esp, .disp = 4}); // arg1.
as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8}); // arg2.
as.Cmpl(Assembler::edx, Assembler::ecx);
as.Jcc(Assembler::Condition::kEqual, equal);
as.Jcc(Assembler::Condition::kBelow, below);
as.Jcc(Assembler::Condition::kAbove, above);
as.Movl(Assembler::eax, 13);
as.Jmp(done);
as.Bind(&equal);
as.Movl(Assembler::eax, 0u);
as.Jmp(done);
as.Bind(&below);
as.Movl(Assembler::eax, -1);
as.Jmp(done);
as.Bind(&above);
as.Movl(Assembler::eax, 1);
as.Jmp(done);
as.Bind(&done);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = int(int, int);
auto target_func = exec.get<TestFunc>();
int result = target_func(1, 1);
if (result != 0) {
ALOGE("Bug in jcc(equal): %x", result);
return false;
}
result = target_func(1, 0);
if (result != 1) {
ALOGE("Bug in jcc(above): %x", result);
return false;
}
result = target_func(0, 1);
if (result != -1) {
ALOGE("Bug in jcc(below): %x", result);
return false;
}
return true;
}
bool ShiftTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
as.Shll(Assembler::eax, int8_t{2});
as.Shrl(Assembler::eax, int8_t{1});
as.Movl(Assembler::ecx, 3);
as.ShllByCl(Assembler::eax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(uint32_t);
uint32_t result = exec.get<TestFunc>()(22);
return result == (22 << 4);
}
bool LogicTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
as.Movl(Assembler::ecx, 0x1);
as.Xorl(Assembler::eax, Assembler::ecx);
as.Movl(Assembler::ecx, 0xf);
as.Andl(Assembler::eax, Assembler::ecx);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(uint32_t);
uint32_t result = exec.get<TestFunc>()(239);
return result == ((239 ^ 1) & 0xf);
}
bool BsrTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 4});
as.Movl(Assembler::edx, 239);
as.Bsrl(Assembler::eax, Assembler::ecx);
as.Cmovl(Assembler::Condition::kZero, Assembler::eax, Assembler::edx);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(uint32_t arg);
auto func = exec.get<TestFunc>();
return func(0) == 239 && func(1 << 15) == 15;
}
bool CallFPTest() {
MachineCode code;
CodeEmitter as(&code);
as.Push(0x3f800000);
as.Push(0x40000000);
as.Call(bit_cast<const void*>(&FloatFunc));
as.Fstps({.base = Assembler::esp});
as.Pop(Assembler::eax);
as.Addl(Assembler::esp, 4);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x3f800000;
}
bool XmmTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::eax, 0x3f800000);
as.Movd(Assembler::xmm0, Assembler::eax);
as.Movl(Assembler::eax, 0x40000000);
as.Movd(Assembler::xmm5, Assembler::eax);
as.Addss(Assembler::xmm0, Assembler::xmm5);
as.Movd(Assembler::eax, Assembler::xmm0);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x40400000;
}
bool ReadGlobalTest() {
MachineCode code;
CodeEmitter as(&code);
static const uint32_t kData[] __attribute__((aligned(16))) = // NOLINT
{0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
as.Movsd(Assembler::xmm0, {.disp = bit_cast<int32_t>(&kData)});
as.Movdqa(Assembler::xmm1, {.disp = bit_cast<int32_t>(&kData)});
as.Movl(Assembler::eax, {.base = Assembler::esp, .disp = 4});
as.Movl(Assembler::ecx, {.base = Assembler::esp, .disp = 8});
as.Movsd({.base = Assembler::eax}, Assembler::xmm0);
as.Movdqu({.base = Assembler::ecx}, Assembler::xmm1);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = void(void*, void*);
uint8_t res1[8];
uint8_t res2[16];
exec.get<TestFunc>()(res1, res2);
return (memcmp(res1, kData, 8) == 0) && (memcmp(res2, kData, 16) == 0);
}
} // namespace x86_32
#elif defined(__amd64__)
namespace x86_64 {
bool LabelTest() {
MachineCode code;
CodeEmitter as(&code);
Assembler::Label skip, skip2, back, end;
as.Call(bit_cast<const void*>(&Callee));
as.Jmp(skip);
as.Movl(Assembler::rax, 2);
as.Bind(&skip);
as.Addb(Assembler::rax, {end});
as.Jmp(skip2);
as.Bind(&back);
as.Addl(Assembler::rax, 12);
as.Jmp(end);
as.Bind(&skip2);
as.Jmp(back);
as.Bind(&end);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = int();
int result = exec.get<TestFunc>()();
return result == uint8_t(239 + 0xc3) + 12;
}
bool CondTest1() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rax, 0xcccccccc);
as.Cmpl(Assembler::rdi, Assembler::rsi);
as.Setcc(Assembler::Condition::kEqual, Assembler::rax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
std::string code_str;
code.AsString(&code_str, InstructionSize::OneByte);
using TestFunc = uint32_t(int, int);
auto target_func = exec.get<TestFunc>();
uint32_t result;
result = target_func(1, 2);
if (result != 0xcccccc00) {
ALOGE("Bug in seteq(not equal): %x", result);
return false;
}
result = target_func(-1, -1);
if (result != 0xcccccc01) {
ALOGE("Bug in seteq(equal): %x", result);
return false;
}
return true;
}
bool CondTest2() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rdx, Assembler::rdi); // arg1.
as.Movl(Assembler::rcx, Assembler::rsi); // arg2.
as.Xorl(Assembler::rax, Assembler::rax);
as.Testb(Assembler::rdx, Assembler::rcx);
as.Setcc(Assembler::Condition::kNotZero, Assembler::rax);
as.Xchgq(Assembler::rax, Assembler::rcx);
as.Xchgq(Assembler::rcx, Assembler::rax);
as.Xchgq(Assembler::rcx, Assembler::r11);
as.Xchgq(Assembler::r11, Assembler::rcx);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(int, int);
auto target_func = exec.get<TestFunc>();
uint32_t result = target_func(0x11, 1);
if (result != 0x1) {
printf("Bug in testb(not zero): %x", result);
return false;
}
result = target_func(0x11, 0x8);
if (result != 0x0) {
printf("Bug in testb(zero): %x", result);
return false;
}
return true;
}
bool JccTest() {
MachineCode code;
CodeEmitter as(&code);
Assembler::Label equal, above, below, done;
as.Cmpl(Assembler::rdi, Assembler::rsi);
as.Jcc(Assembler::Condition::kEqual, equal);
as.Jcc(Assembler::Condition::kBelow, below);
as.Jcc(Assembler::Condition::kAbove, above);
as.Movl(Assembler::rax, 13);
as.Jmp(done);
as.Bind(&equal);
as.Movq(Assembler::rax, 0);
as.Jmp(done);
as.Bind(&below);
as.Movl(Assembler::rax, -1);
as.Jmp(done);
as.Bind(&above);
as.Movl(Assembler::rax, 1);
as.Jmp(done);
as.Bind(&done);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = int(int, int);
auto target_func = exec.get<TestFunc>();
int result;
result = target_func(1, 1);
if (result != 0) {
ALOGE("Bug in jcc(equal): %x", result);
return false;
}
result = target_func(1, 0);
if (result != 1) {
ALOGE("Bug in jcc(above): %x", result);
return false;
}
result = target_func(0, 1);
if (result != -1) {
ALOGE("Bug in jcc(below): %x", result);
return false;
}
return true;
}
bool ReadWriteTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movq(Assembler::rax, 0);
as.Movb(Assembler::rax, {.base = Assembler::rdi});
as.Movl(Assembler::rcx, {.base = Assembler::rsi});
as.Addl(Assembler::rax, Assembler::rcx);
as.Movl({.base = Assembler::rsi}, Assembler::rax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t(uint8_t*, uint32_t*);
uint8_t p1[4] = {0x12, 0x34, 0x56, 0x78};
uint32_t p2 = 0x239;
uint32_t result = exec.get<TestFunc>()(p1, &p2);
return (result == 0x239 + 0x12) && (p2 == result);
}
bool CallFPTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rax, 0x40000000);
as.Movd(Assembler::xmm0, Assembler::rax);
as.Movl(Assembler::rax, 0x3f800000);
as.Movd(Assembler::xmm1, Assembler::rax);
as.Call(bit_cast<const void*>(&FloatFunc));
as.Movd(Assembler::rax, Assembler::xmm0);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x3f800000;
}
bool XmmTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rax, 0x40000000);
as.Movd(Assembler::xmm0, Assembler::rax);
as.Movl(Assembler::rax, 0x3f800000);
as.Movd(Assembler::xmm11, Assembler::rax);
as.Addss(Assembler::xmm0, Assembler::xmm11);
as.Movaps(Assembler::xmm12, Assembler::xmm0);
as.Addss(Assembler::xmm0, Assembler::xmm12);
as.Movapd(Assembler::xmm14, Assembler::xmm1);
as.Movd(Assembler::rax, Assembler::xmm0);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x40c00000;
}
bool XmmMemTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movsd(Assembler::xmm0, {.base = Assembler::rdi});
as.Movaps(Assembler::xmm12, Assembler::xmm0);
as.Addsd(Assembler::xmm12, Assembler::xmm12);
as.Movsd({.base = Assembler::rdi}, Assembler::xmm12);
as.Movq(Assembler::rax, Assembler::xmm0);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
double d = 239.0;
char bits[16], *p = bits + 5;
memcpy(p, &d, sizeof(d));
using TestFunc = uint64_t(char* p);
uint64_t result = exec.get<TestFunc>()(p);
uint64_t doubled = *reinterpret_cast<uint64_t*>(p);
return result == 0x406de00000000000ULL && doubled == 0x407de00000000000ULL;
}
bool MovsxblRexTest() {
MachineCode code;
CodeEmitter as(&code);
as.Xorl(Assembler::rdx, Assembler::rdx);
as.Movl(Assembler::rsi, 0xdeadff);
// CodeEmitter should use REX prefix to encode SIL.
// Without REX DH is used.
as.Movsxbl(Assembler::rax, Assembler::rsi);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0xffffffff;
}
bool MovzxblRexTest() {
MachineCode code;
CodeEmitter as(&code);
as.Xorl(Assembler::rdx, Assembler::rdx);
as.Movl(Assembler::rsi, 0xdeadff);
// CodeEmitter should use REX prefix to encode SIL.
// Without REX DH is used.
as.Movzxbl(Assembler::rax, Assembler::rsi);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x000000ff;
}
bool ShldlRexTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rdx, 0x12345678);
// If most-significant bit is not encoded correctly with REX
// RAX can be used instead R8 and R10 can be used instead RDX.
// Init them all:
as.Xorl(Assembler::r8, Assembler::r8);
as.Movl(Assembler::rax, 0xdeadbeef);
as.Movl(Assembler::r10, 0xdeadbeef);
as.Shldl(Assembler::r8, Assembler::rdx, int8_t{8});
as.Movl(Assembler::rcx, 8);
as.ShldlByCl(Assembler::r8, Assembler::rdx);
as.Movl(Assembler::rax, Assembler::r8);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x1212;
}
bool ShrdlRexTest() {
MachineCode code;
CodeEmitter as(&code);
as.Movl(Assembler::rdx, 0x12345678);
// If most-significant bit is not encoded correctly with REX
// RAX can be used instead R8 and R10 can be used instead RDX.
// Init them all:
as.Xorl(Assembler::r8, Assembler::r8);
as.Movl(Assembler::rax, 0xdeadbeef);
as.Movl(Assembler::r10, 0xdeadbeef);
as.Shrdl(Assembler::r8, Assembler::rdx, int8_t{8});
as.Movl(Assembler::rcx, 8);
as.ShrdlByCl(Assembler::r8, Assembler::rdx);
as.Movl(Assembler::rax, Assembler::r8);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = uint32_t();
uint32_t result = exec.get<TestFunc>()();
return result == 0x78780000;
}
bool ReadGlobalTest() {
MachineCode code;
CodeEmitter as(&code);
static const uint32_t kData[] __attribute__((aligned(16))) = // NOLINT
{0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff};
// We couldn't read data from arbitrary address on x86_64, need address in first 2GiB.
void* data =
mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
// Copy our global there.
memcpy(data, kData, 16);
int32_t data_offset = static_cast<int32_t>(bit_cast<intptr_t>(data));
as.Movsd(Assembler::xmm0, {.disp = data_offset});
as.Movdqa(Assembler::xmm1, {.disp = data_offset});
as.Movsd({.base = Assembler::rdi}, Assembler::xmm0);
as.Movdqu({.base = Assembler::rsi}, Assembler::xmm1);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = void(void*, void*);
uint8_t res1[8];
uint8_t res2[16];
exec.get<TestFunc>()(res1, res2);
munmap(data, 4096);
return (memcmp(res1, kData, 8) == 0) && (memcmp(res2, kData, 16) == 0);
}
bool MemShiftTest() {
MachineCode code;
CodeEmitter as(&code);
as.Push(Assembler::rdi);
as.Movl(Assembler::rcx, 1);
as.ShrlByCl({.base = Assembler::rsp});
as.Addl(Assembler::rcx, 1);
as.Movq(Assembler::rdi, Assembler::rsp);
as.ShllByCl({.base = Assembler::rdi});
as.Pop(Assembler::rax);
as.Ret();
as.Finalize();
ScopedExecRegion exec(&code);
using TestFunc = int(int x);
int result = exec.get<TestFunc>()(0x10);
return result == 0x20;
}
} // namespace x86_64
#endif
#if defined(__i386__) || defined(__amd64__)
#if defined(__i386__)
extern "C" const uint8_t berberis_gnu_as_output_start[] asm(
"berberis_gnu_as_output_start_x86_32");
extern "C" const uint8_t berberis_gnu_as_output_end[] asm(
"berberis_gnu_as_output_end_x86_32");
namespace x86_32 {
void GenInsnsCommon(CodeEmitter* as);
void GenInsnsArch(CodeEmitter* as);
} // namespace x86_32
#else
extern "C" const uint8_t berberis_gnu_as_output_start[] asm(
"berberis_gnu_as_output_start_x86_64");
extern "C" const uint8_t berberis_gnu_as_output_end[] asm(
"berberis_gnu_as_output_end_x86_64");
namespace x86_64 {
void GenInsnsCommon(CodeEmitter* as);
void GenInsnsArch(CodeEmitter* as);
} // namespace x86_64
#endif
bool ExhaustiveTest() {
MachineCode code;
CodeEmitter as(&code);
#if defined(__i386__)
berberis::x86_32::GenInsnsCommon(&as);
berberis::x86_32::GenInsnsArch(&as);
#else
berberis::x86_64::GenInsnsCommon(&as);
berberis::x86_64::GenInsnsArch(&as);
#endif
as.Finalize();
return CompareCode(
berberis_gnu_as_output_start, berberis_gnu_as_output_end, code, CPUArch::kX86_64);
}
bool MixedAssembler() {
MachineCode code;
x86_32::Assembler as32(&code);
x86_64::Assembler as64(&code);
x86_32::Assembler::Label lbl32;
x86_64::Assembler::Label lbl64;
as32.Jmp(lbl32);
as32.Xchgl(x86_32::Assembler::eax, x86_32::Assembler::eax);
as64.Jmp(lbl64);
as64.Xchgl(x86_64::Assembler::rax, x86_64::Assembler::rax);
as32.Bind(&lbl32);
as32.Movl(x86_32::Assembler::eax, {.disp = 0});
as64.Bind(&lbl64);
as32.Finalize();
as64.Finalize();
// clang-format off
static const uint8_t kCodeTemplate[] = {
0xe9, 0x08, 0x00, 0x00, 0x00, // jmp lbl32
0x90, // xchg %eax, %eax == nop
0xe9, 0x07, 0x00, 0x00, 0x00, // jmp lbl64
0x87, 0xc0, // xchg %eax, %eax != nop
// lbl32:
0xa1, 0x00, 0x00, 0x00, 0x00 // movabs %eax, 0x0
// lbl64:
};
// clang-format on
return CompareCode(std::begin(kCodeTemplate), std::end(kCodeTemplate), code, CPUArch::kX86_64);
}
#endif
} // namespace berberis
TEST(Assembler, AssemblerTest) {
EXPECT_TRUE(berberis::rv32::AssemblerTest());
EXPECT_TRUE(berberis::rv64::AssemblerTest());
EXPECT_TRUE(berberis::x86_32::AssemblerTest());
EXPECT_TRUE(berberis::x86_64::AssemblerTest());
#if defined(__i386__)
EXPECT_TRUE(berberis::x86_32::LabelTest());
EXPECT_TRUE(berberis::x86_32::CondTest1());
EXPECT_TRUE(berberis::x86_32::CondTest2());
EXPECT_TRUE(berberis::x86_32::JccTest());
EXPECT_TRUE(berberis::x86_32::ShiftTest());
EXPECT_TRUE(berberis::x86_32::LogicTest());
EXPECT_TRUE(berberis::x86_32::CallFPTest());
EXPECT_TRUE(berberis::x86_32::XmmTest());
EXPECT_TRUE(berberis::x86_32::BsrTest());
EXPECT_TRUE(berberis::x86_32::ReadGlobalTest());
#elif defined(__amd64__)
EXPECT_TRUE(berberis::x86_64::LabelTest());
EXPECT_TRUE(berberis::x86_64::CondTest1());
EXPECT_TRUE(berberis::x86_64::CondTest2());
EXPECT_TRUE(berberis::x86_64::JccTest());
EXPECT_TRUE(berberis::x86_64::ReadWriteTest());
EXPECT_TRUE(berberis::x86_64::CallFPTest());
EXPECT_TRUE(berberis::x86_64::XmmTest());
EXPECT_TRUE(berberis::x86_64::XmmMemTest());
EXPECT_TRUE(berberis::x86_64::MovsxblRexTest());
EXPECT_TRUE(berberis::x86_64::MovzxblRexTest());
EXPECT_TRUE(berberis::x86_64::ShldlRexTest());
EXPECT_TRUE(berberis::x86_64::ShrdlRexTest());
EXPECT_TRUE(berberis::x86_64::ReadGlobalTest());
EXPECT_TRUE(berberis::x86_64::MemShiftTest());
#endif
// Currently we don't support these tests for riscv.
// TODO(b/352784623): Implement for riscv.
#if defined(__i386__) || defined(__x86_64__)
EXPECT_TRUE(berberis::ExhaustiveTest());
EXPECT_TRUE(berberis::MixedAssembler());
#endif
}