335 lines
12 KiB
C
335 lines
12 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ This program is free software; you can redistribute it and/or modify │
|
|
│ it under the terms of the GNU General Public License as published by │
|
|
│ the Free Software Foundation; version 2 of the License. │
|
|
│ │
|
|
│ This program is distributed in the hope that it will be useful, but │
|
|
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
|
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
|
│ General Public License for more details. │
|
|
│ │
|
|
│ You should have received a copy of the GNU General Public License │
|
|
│ along with this program; if not, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/bits/progn.h"
|
|
#include "libc/fmt/bing.h"
|
|
#include "libc/math.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/runtime/gc.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/testlib/ezbench.h"
|
|
#include "libc/testlib/testlib.h"
|
|
#include "libc/x/x.h"
|
|
#include "tool/build/lib/endian.h"
|
|
#include "tool/build/lib/fpu.h"
|
|
#include "tool/build/lib/machine.h"
|
|
#include "tool/build/lib/memory.h"
|
|
|
|
const uint8_t kPi80[] = {
|
|
0xd9, 0xe8, // fld1
|
|
0xb8, 0x0a, 0x00, 0x00, 0x00, // mov $0xa,%eax
|
|
0x31, 0xd2, // xor %edx,%edx
|
|
0xd9, 0xee, // fldz
|
|
0x48, 0x98, // cltq
|
|
0x48, 0x39, 0xc2, // cmp %rax,%rdx
|
|
0xd9, 0x05, 0x1a, 0x00, 0x00, 0x00, // flds 0x1a(%rip)
|
|
0x7d, 0x13, // jge 2b <pi80+0x2b>
|
|
0xde, 0xc1, // faddp
|
|
0x48, 0xff, 0xc2, // inc %rdx
|
|
0xd9, 0xfa, // fsqrt
|
|
0xd9, 0x05, 0x0f, 0x00, 0x00, 0x00, // flds 15(%rip)
|
|
0xd8, 0xc9, // fmul %st(1),%st
|
|
0xde, 0xca, // fmulp %st,%st(2)
|
|
0xeb, 0xe2, // jmp d <pi80+0xd>
|
|
0xdd, 0xd9, // fstp %st(1)
|
|
0xde, 0xf1, // fdivp
|
|
0xf4, // hlt
|
|
0x00, 0x00, 0x00, 0x40, // .float 2.0
|
|
0x00, 0x00, 0x00, 0x3f, // .float 0.5
|
|
};
|
|
|
|
const uint8_t kTenthprime[] = {
|
|
0x31, 0xd2, // xor %edx,%edx
|
|
0x45, 0x31, 0xc0, // xor %r8d,%r8d
|
|
0x31, 0xc9, // xor %ecx,%ecx
|
|
0xbe, 0x03, 0x00, 0x00, 0x00, // mov $0x3,%esi
|
|
0x41, 0xff, 0xc0, // inc %r8d
|
|
0x44, 0x89, 0xc0, // mov %r8d,%eax
|
|
0x83, 0xf9, 0x0a, // cmp $0xa,%ecx
|
|
0x74, 0x0b, // je 20
|
|
0x99, // cltd
|
|
0xf7, 0xfe, // idiv %esi
|
|
0x83, 0xfa, 0x01, // cmp $0x1,%edx
|
|
0x83, 0xd9, 0xff, // sbb $-1,%ecx
|
|
0xeb, 0xea, // jmp a
|
|
0xf4, // hlt
|
|
};
|
|
|
|
const uint8_t kTenthprime2[] = {
|
|
0xE8, 0x11, 0x00, 0x00, 0x00, //
|
|
0xF4, //
|
|
0x89, 0xF8, //
|
|
0xB9, 0x03, 0x00, 0x00, 0x00, //
|
|
0x99, //
|
|
0xF7, 0xF9, //
|
|
0x85, 0xD2, //
|
|
0x0F, 0x95, 0xC0, //
|
|
0xC3, //
|
|
0x55, //
|
|
0x48, 0x89, 0xE5, //
|
|
0x31, 0xF6, //
|
|
0x45, 0x31, 0xC0, //
|
|
0x44, 0x89, 0xC7, //
|
|
0xE8, 0xDF, 0xFF, 0xFF, 0xFF, //
|
|
0x0F, 0xB6, 0xC0, //
|
|
0x66, 0x83, 0xF8, 0x01, //
|
|
0x83, 0xDE, 0xFF, //
|
|
0x41, 0xFF, 0xC0, //
|
|
0x83, 0xFE, 0x0A, //
|
|
0x75, 0xE6, //
|
|
0x44, 0x89, 0xC0, //
|
|
0x5D, //
|
|
0xC3, //
|
|
};
|
|
|
|
struct Machine *m;
|
|
|
|
void SetUp(void) {
|
|
m = NewMachine();
|
|
m->ip = 0;
|
|
ReserveVirtual(m, 0, 4096);
|
|
ASSERT_EQ(0x5000, m->real.i);
|
|
ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T
|
|
ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT
|
|
ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE
|
|
ASSERT_EQ(0x4007, Read64(m->real.p + 0x3000)); // PT
|
|
Write64(m->sp, 4096);
|
|
}
|
|
|
|
void TearDown(void) {
|
|
FreeVirtual(m, 0, 4096);
|
|
ASSERT_EQ(0x5000, m->real.i);
|
|
ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T
|
|
ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT
|
|
ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE
|
|
ASSERT_EQ(0x0000, Read64(m->real.p + 0x3000)); // PT
|
|
ASSERT_EQ(0x4000, m->realfree->i);
|
|
ASSERT_EQ(0x1000, m->realfree->n);
|
|
FreeMachine(m);
|
|
}
|
|
|
|
int ExecuteUntilHalt(struct Machine *m) {
|
|
int rc;
|
|
if (!(rc = setjmp(m->onhalt))) {
|
|
for (;;) {
|
|
LoadInstruction(m);
|
|
ExecuteInstruction(m);
|
|
}
|
|
} else {
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
TEST(machine, test) {
|
|
VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime));
|
|
ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m));
|
|
ASSERT_EQ(15, Read32(m->ax));
|
|
}
|
|
|
|
TEST(machine, testFpu) {
|
|
VirtualRecv(m, 0, kPi80, sizeof(kPi80));
|
|
ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m));
|
|
ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001);
|
|
m->ip = 0;
|
|
ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m));
|
|
ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001);
|
|
}
|
|
|
|
BENCH(machine, benchPrimeNumberPrograms) {
|
|
VirtualRecv(m, 0, kTenthprime2, sizeof(kTenthprime2));
|
|
EZBENCH2("tenthprime2", m->ip = 0, ExecuteUntilHalt(m));
|
|
ASSERT_EQ(15, Read32(m->ax));
|
|
VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime));
|
|
EZBENCH2("tenthprime", m->ip = 0, ExecuteUntilHalt(m));
|
|
ASSERT_EQ(15, Read32(m->ax));
|
|
}
|
|
|
|
BENCH(machine, benchFpu) {
|
|
VirtualRecv(m, 0, kPi80, sizeof(kPi80));
|
|
EZBENCH2("pi80", m->ip = 0, PROGN(ExecuteUntilHalt(m), FpuPop(m)));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec2) {
|
|
uint8_t kMovCode[] = {0xbe, 0x03, 0x00, 0x00, 0x00};
|
|
VirtualRecv(m, 0, kMovCode, sizeof(kMovCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("mov", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec3) {
|
|
uint8_t kMovdCode[] = {0x66, 0x0f, 0x6e, 0xc0};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kMovdCode, sizeof(kMovdCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("movd", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec4) {
|
|
uint8_t kAddpsRegregCode[] = {0x0f, 0x58, 0xC0};
|
|
uint8_t kAddpsMemregCode[] = {0x0f, 0x58, 0x00};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kAddpsRegregCode, sizeof(kAddpsRegregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addps reg reg", m->ip = 0, ExecuteInstruction(m));
|
|
VirtualRecv(m, 0, kAddpsMemregCode, sizeof(kAddpsMemregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addps mem reg", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec5) {
|
|
uint8_t kPaddwRegregCode[] = {0x66, 0x0F, 0xFD, 0xC0};
|
|
uint8_t kPaddwMemregCode[] = {0x66, 0x0F, 0xFD, 0x00};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kPaddwRegregCode, sizeof(kPaddwRegregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("paddw", m->ip = 0, ExecuteInstruction(m));
|
|
VirtualRecv(m, 0, kPaddwMemregCode, sizeof(kPaddwMemregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("paddw mem", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec6) {
|
|
uint8_t kPsubqRegregCode[] = {0x66, 0x0F, 0xFB, 0xC0};
|
|
uint8_t kPsubqMemregCode[] = {0x66, 0x0F, 0xFB, 0x00};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kPsubqRegregCode, sizeof(kPsubqRegregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("psubq", m->ip = 0, ExecuteInstruction(m));
|
|
VirtualRecv(m, 0, kPsubqMemregCode, sizeof(kPsubqMemregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("psubq mem", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchAddqMem) {
|
|
uint8_t kAddMemregCode[] = {0x48, 0x03, 0x08};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addq mem", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchAddlMem) {
|
|
uint8_t kAddMemregCode[] = {0x03, 0x08};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addl mem", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchAddq) {
|
|
uint8_t kAddqCode[] = {0x48, 0x01, 0xd8};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kAddqCode, sizeof(kAddqCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addq", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchAddb) {
|
|
uint8_t kAddbCode[] = {0x00, 0xd8};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kAddbCode, sizeof(kAddbCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("addb", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchXorReg) {
|
|
VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime));
|
|
LoadInstruction(m);
|
|
EZBENCH2("xor", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchLoadExec8) {
|
|
uint8_t kFchsCode[] = {0xd9, 0xe0};
|
|
Write64(m->ax, 0);
|
|
OpFinit(m);
|
|
*FpuSt(m, 0) = M_PI;
|
|
FpuSetTag(m, 0, kFpuTagValid);
|
|
VirtualRecv(m, 0, kFchsCode, sizeof(kFchsCode));
|
|
LoadInstruction(m);
|
|
EZBENCH2("fchs", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchPushpop) {
|
|
uint8_t kPushpop[] = {0x50, 0x58};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kPushpop, sizeof(kPushpop));
|
|
EZBENCH2("pushpop", m->ip = 0,
|
|
PROGN(LoadInstruction(m), ExecuteInstruction(m), LoadInstruction(m),
|
|
ExecuteInstruction(m)));
|
|
}
|
|
|
|
BENCH(machine, benchPause) {
|
|
uint8_t kPause[] = {0xf3, 0x90};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kPause, sizeof(kPause));
|
|
LoadInstruction(m);
|
|
EZBENCH2("pause", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchClc) {
|
|
uint8_t kClc[] = {0xf8};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kClc, sizeof(kClc));
|
|
LoadInstruction(m);
|
|
EZBENCH2("clc", m->ip = 0, ExecuteInstruction(m));
|
|
}
|
|
|
|
BENCH(machine, benchNop) {
|
|
uint8_t kNop[] = {0x90};
|
|
Write64(m->ax, 0);
|
|
VirtualRecv(m, 0, kNop, sizeof(kNop));
|
|
LoadInstruction(m);
|
|
EZBENCH2("nop", m->ip = 0, ExecuteInstruction(m));
|
|
EZBENCH2("nop w/ load", m->ip = 0,
|
|
PROGN(LoadInstruction(m), ExecuteInstruction(m)));
|
|
}
|
|
|
|
TEST(x87, fprem1) {
|
|
// 1 rem -1.5
|
|
const uint8_t prog[] = {
|
|
0xd9, 0x05, 0x05, 0x00, 0x00, 0x00, // flds
|
|
0xd9, 0xe8, // fld1
|
|
0xd9, 0xf8, // fprem
|
|
0xf4, // hlt
|
|
0x00, 0x00, 0xc0, 0xbf, // .float -1.5
|
|
};
|
|
VirtualRecv(m, 0, prog, sizeof(prog));
|
|
ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m));
|
|
ASSERT_LDBL_EQ(1, FpuPop(m));
|
|
}
|
|
|
|
TEST(x87, fprem2) {
|
|
// 12300000000000000. rem .0000000000000123
|
|
const uint8_t prog[] = {
|
|
0xdd, 0x05, 0x11, 0x00, 0x00, 0x00, // fldl
|
|
0xdd, 0x05, 0x03, 0x00, 0x00, 0x00, // fldl
|
|
0xd9, 0xf8, // fprem
|
|
0xf4, // hlt
|
|
0x00, 0x60, 0x5e, 0x75, 0x64, 0xd9, 0x45, 0x43, //
|
|
0x5b, 0x14, 0xea, 0x9d, 0x77, 0xb2, 0x0b, 0x3d, //
|
|
};
|
|
VirtualRecv(m, 0, prog, sizeof(prog));
|
|
ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m));
|
|
ASSERT_LDBL_EQ(1.1766221079117338e-14, FpuPop(m));
|
|
}
|
|
|
|
TEST(machine, sizeIsReasonable) {
|
|
ASSERT_LE(sizeof(struct Machine), 65536 * 3);
|
|
}
|