mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-08 15:33:33 +00:00
f4f4caab0e
I wanted a tiny scriptable meltdown proof way to run userspace programs and visualize how program execution impacts memory. It helps to explain how things like Actually Portable Executable works. It can show you how the GCC generated code is going about manipulating matrices and more. I didn't feel fully comfortable with Qemu and Bochs because I'm not smart enough to understand them. I wanted something like gVisor but with much stronger levels of assurances. I wanted a single binary that'll run, on all major operating systems with an embedded GPL barrier ZIP filesystem that is tiny enough to transpile to JavaScript and run in browsers too. https://justine.storage.googleapis.com/emulator625.mp4
606 lines
20 KiB
C
606 lines
20 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/calls/struct/sigaction.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/runtime/gc.h"
|
|
#include "libc/sysv/consts/sa.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/testlib/ezbench.h"
|
|
#include "libc/testlib/testlib.h"
|
|
#include "libc/x/x.h"
|
|
#include "tool/build/lib/divmul.h"
|
|
#include "tool/build/lib/endian.h"
|
|
#include "tool/build/lib/flags.h"
|
|
|
|
#define CX 1
|
|
#define OSZ 00000000040
|
|
#define REXW 00000004000
|
|
#define RM(x) (0000000700 & ((x) << 006))
|
|
#define MOD(x) (0060000000 & ((x) << 026))
|
|
|
|
jmp_buf sigfpejmp;
|
|
struct Machine m[1];
|
|
struct sigaction oldsigfpe[1];
|
|
struct XedDecodedInst xedd[1];
|
|
|
|
void OnSigFpe(void) {
|
|
/* ProTip: gdb -ex 'handle SIGFPE nostop noprint pass' */
|
|
longjmp(sigfpejmp, 1);
|
|
}
|
|
|
|
void SetUp(void) {
|
|
m->xedd = xedd;
|
|
InitMachine(m);
|
|
CHECK_NE(-1, xsigaction(SIGFPE, OnSigFpe, SA_NODEFER, 0, oldsigfpe));
|
|
}
|
|
|
|
void TearDown(void) {
|
|
m->xedd = xedd;
|
|
CHECK_NE(-1, sigaction(SIGFPE, oldsigfpe, NULL));
|
|
}
|
|
|
|
TEST(imul8, test) {
|
|
static const uint8_t A[] = {0x00, 0x01, 0x80, 0x7F, 0x81, 0x7E, 0xFF, 0xBF};
|
|
int i, j;
|
|
int16_t ax;
|
|
bool cf, of;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write8(m->ax, A[i]);
|
|
Write8(m->cx, A[j]);
|
|
OpMulAxAlEbSigned(m);
|
|
asm volatile("imulb\t%3"
|
|
: "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "q"(A[j]), "0"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ(ax, (int16_t)Read16(m->ax));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(imul16, test) {
|
|
static const uint16_t A[] = {0x0000, 0x0001, 0x8000, 0x7FFF, 0x8001, 0x7FFE,
|
|
0xFFFF, 0xBeef, 0x00b5, 0x00b6, 0xb504, 0xb505};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint16_t dx, ax;
|
|
m->xedd->op.rde = OSZ | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write16(m->ax, A[i]);
|
|
Write16(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpSigned(m);
|
|
asm("imulw\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((int32_t)((uint32_t)dx << 16 | ax),
|
|
(int32_t)((uint32_t)Read16(m->dx) << 16 | Read16(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(imul32, test) {
|
|
static const uint32_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0xDeadBeef,
|
|
0x000000b6, 0x0000b504, 0x0000b505, 0xb504f334};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint32_t dx, ax;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write32(m->ax, A[i]);
|
|
Write32(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpSigned(m);
|
|
asm("imull\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((int64_t)((uint64_t)dx << 32 | ax),
|
|
(int64_t)((uint64_t)Read32(m->dx) << 32 | Read32(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(imul64, test) {
|
|
static const uint64_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0xDeadBeef,
|
|
0x000000b6, 0x0000b504, 0x0000b505, 0xb504f334};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint64_t dx, ax;
|
|
m->xedd->op.rde = REXW | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write64(m->ax, A[i]);
|
|
Write64(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpSigned(m);
|
|
asm("imulq\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((int128_t)((uint128_t)dx << 64 | ax),
|
|
(int128_t)((uint128_t)Read64(m->dx) << 64 | Read64(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(mul8, test) {
|
|
static const uint8_t A[] = {0x00, 0x01, 0x80, 0x7F, 0x81, 0x7E, 0xFF, 0xb6};
|
|
int i, j;
|
|
uint16_t ax;
|
|
bool cf, of;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write8(m->ax, A[i]);
|
|
Write8(m->cx, A[j]);
|
|
OpMulAxAlEbUnsigned(m);
|
|
asm volatile("mulb\t%3"
|
|
: "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "q"(A[j]), "0"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ(ax, Read16(m->ax));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(mul16, test) {
|
|
static const uint16_t A[] = {0x0000, 0x0001, 0x8000, 0x7FFF,
|
|
0x8001, 0x7FFE, 0xFFFF, 0x00b6};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint16_t dx, ax;
|
|
m->xedd->op.rde = OSZ | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write16(m->ax, A[i]);
|
|
Write16(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpUnsigned(m);
|
|
asm("mulw\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((uint32_t)((uint32_t)dx << 16 | ax),
|
|
(uint32_t)((uint32_t)Read16(m->dx) << 16 | Read16(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(mul32, test) {
|
|
static const uint32_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0x000000b5,
|
|
0x000000b6, 0x0000b504, 0x0000b505, 0xb504f334};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint32_t dx, ax;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write32(m->ax, A[i]);
|
|
Write32(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpUnsigned(m);
|
|
asm("mull\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((uint64_t)((uint64_t)dx << 32 | ax),
|
|
(uint64_t)((uint64_t)Read32(m->dx) << 32 | Read32(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(mul64, test) {
|
|
static const uint64_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0x000000b6,
|
|
0x0000b504, 0x0000b505, 0xb504f333, 0xb504f334};
|
|
int i, j;
|
|
bool cf, of;
|
|
uint64_t dx, ax;
|
|
m->xedd->op.rde = REXW | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
Write64(m->ax, A[i]);
|
|
Write64(m->cx, A[j]);
|
|
OpMulRdxRaxEvqpUnsigned(m);
|
|
asm("mulq\t%4"
|
|
: "=d"(dx), "=a"(ax), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(A[j]), "1"(A[i])
|
|
: "cc");
|
|
EXPECT_EQ((uint128_t)((uint128_t)dx << 64 | ax),
|
|
(uint128_t)((uint128_t)Read64(m->dx) << 64 | Read64(m->ax)));
|
|
EXPECT_EQ(cf, GetFlag(m->flags, FLAGS_CF));
|
|
EXPECT_EQ(of, GetFlag(m->flags, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(idiv8, test) {
|
|
static const uint8_t A[] = {0x00, 0x01, 0x80, 0x7F, 0x81, 0x7E, 0xFF, 0xBF};
|
|
uint16_t remquo;
|
|
bool gotthrow, gotsigfpe;
|
|
int8_t i, j, k, w, x, a, b;
|
|
int8_t quotient, remainder;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
m->ax[1] = A[i];
|
|
m->ax[0] = A[j];
|
|
m->cx[0] = A[k];
|
|
gotthrow = false;
|
|
gotsigfpe = false;
|
|
if (!setjmp(m->onhalt)) {
|
|
OpDivAlAhAxEbSigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
asm("idivb\t%1"
|
|
: "=a"(remquo)
|
|
: "q"(A[k]), "0"((int16_t)(A[i] << 8 | A[j]))
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
quotient = (int8_t)remquo;
|
|
remainder = (int8_t)(remquo >> 8);
|
|
EXPECT_EQ(quotient, (int8_t)m->ax[0]);
|
|
EXPECT_EQ(remainder, (int8_t)m->ax[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(idiv16, test) {
|
|
static const uint16_t A[] = {0x0000, 0x0001, 0x8000, 0x7FFF,
|
|
0x8001, 0x7FFE, 0xFFFF, 0xBeef};
|
|
bool gotthrow, gotsigfpe;
|
|
int16_t i, j, k, w, x, a, b;
|
|
int16_t quotient, remainder;
|
|
m->xedd->op.rde = OSZ | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 2);
|
|
memcpy(m->ax, &A[j], 2);
|
|
memcpy(m->cx, &A[k], 2);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpSigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("idivw\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (int16_t)Read16(m->ax));
|
|
EXPECT_EQ(remainder, (int16_t)Read16(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(idiv32, test) {
|
|
static const uint32_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0xDeadBeef};
|
|
bool gotthrow, gotsigfpe;
|
|
int32_t i, j, k, w, x, a, b;
|
|
int32_t quotient, remainder;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 4);
|
|
memcpy(m->ax, &A[j], 4);
|
|
memcpy(m->cx, &A[k], 4);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpSigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("idivl\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (int32_t)Read32(m->ax));
|
|
EXPECT_EQ(remainder, (int32_t)Read32(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(idiv64, test) {
|
|
static const uint64_t A[] = {0x0000000000000000, 0x0000000000000001,
|
|
0x8000000000000000, 0x7FFFFFFFFFFFFFFF,
|
|
0x8000000000000001, 0x7FFFFFFFFFFFFFFE,
|
|
0xFFFFFFFFFFFFFFFF, 0x00DeadBeefCafe00};
|
|
bool gotthrow, gotsigfpe;
|
|
int64_t i, j, k, w, x, a, b;
|
|
int64_t quotient, remainder;
|
|
m->xedd->op.rde = REXW | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 8);
|
|
memcpy(m->ax, &A[j], 8);
|
|
memcpy(m->cx, &A[k], 8);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpSigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("idivq\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (int64_t)Read64(m->ax));
|
|
EXPECT_EQ(remainder, (int64_t)Read64(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(div, test) {
|
|
static const uint8_t A[] = {0x00, 0x01, 0x80, 0x7F, 0x81, 0x7E, 0xFF, 0xBF};
|
|
uint16_t remquo;
|
|
bool gotthrow, gotsigfpe;
|
|
uint8_t i, j, k, w, x, a, b;
|
|
uint8_t quotient, remainder;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
m->ax[1] = A[i];
|
|
m->ax[0] = A[j];
|
|
m->cx[0] = A[k];
|
|
gotthrow = false;
|
|
gotsigfpe = false;
|
|
if (!setjmp(m->onhalt)) {
|
|
OpDivAlAhAxEbUnsigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
asm("divb\t%1"
|
|
: "=a"(remquo)
|
|
: "q"(A[k]), "0"((uint16_t)(A[i] << 8 | A[j]))
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
quotient = (uint8_t)remquo;
|
|
remainder = (uint8_t)(remquo >> 8);
|
|
EXPECT_EQ(quotient, (uint8_t)m->ax[0]);
|
|
EXPECT_EQ(remainder, (uint8_t)m->ax[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(div16, test) {
|
|
static const uint16_t A[] = {0x0000, 0x0001, 0x8000, 0x7FFF,
|
|
0x8001, 0x7FFE, 0xFFFF, 0xBeef};
|
|
bool gotthrow, gotsigfpe;
|
|
uint16_t i, j, k, w, x, a, b;
|
|
uint16_t quotient, remainder;
|
|
m->xedd->op.rde = OSZ | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 2);
|
|
memcpy(m->ax, &A[j], 2);
|
|
memcpy(m->cx, &A[k], 2);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpUnsigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("divw\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (uint16_t)Read16(m->ax));
|
|
EXPECT_EQ(remainder, (uint16_t)Read16(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(div32, test) {
|
|
static const uint32_t A[] = {0x00000000, 0x00000001, 0x80000000, 0x7FFFFFFF,
|
|
0x80000001, 0x7FFFFFFE, 0xFFFFFFFF, 0xDeadBeef};
|
|
bool gotthrow, gotsigfpe;
|
|
uint32_t i, j, k, w, x, a, b;
|
|
uint32_t quotient, remainder;
|
|
m->xedd->op.rde = MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 4);
|
|
memcpy(m->ax, &A[j], 4);
|
|
memcpy(m->cx, &A[k], 4);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpUnsigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("divl\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (uint32_t)Read32(m->ax));
|
|
EXPECT_EQ(remainder, (uint32_t)Read32(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(div64, test) {
|
|
static const uint64_t A[] = {0x0000000000000000, 0x0000000000000001,
|
|
0x8000000000000000, 0x7FFFFFFFFFFFFFFF,
|
|
0x8000000000000001, 0x7FFFFFFFFFFFFFFE,
|
|
0xFFFFFFFFFFFFFFFF, 0x00DeadBeefCafe00};
|
|
bool gotthrow, gotsigfpe;
|
|
uint64_t i, j, k, w, x, a, b;
|
|
uint64_t quotient, remainder;
|
|
m->xedd->op.rde = REXW | MOD(3) | RM(CX);
|
|
for (i = 0; i < ARRAYLEN(A); ++i) {
|
|
for (j = 0; j < ARRAYLEN(A); ++j) {
|
|
for (k = 0; k < ARRAYLEN(A); ++k) {
|
|
memcpy(m->dx, &A[i], 8);
|
|
memcpy(m->ax, &A[j], 8);
|
|
memcpy(m->cx, &A[k], 8);
|
|
if (!setjmp(m->onhalt)) {
|
|
gotthrow = false;
|
|
OpDivRdxRaxEvqpUnsigned(m);
|
|
} else {
|
|
gotthrow = true;
|
|
}
|
|
if (!setjmp(sigfpejmp)) {
|
|
gotsigfpe = false;
|
|
asm("divq\t%2"
|
|
: "=d"(remainder), "=a"(quotient)
|
|
: "r"(A[k]), "0"(A[i]), "1"(A[j])
|
|
: "cc");
|
|
} else {
|
|
gotsigfpe = true;
|
|
}
|
|
EXPECT_EQ(gotsigfpe, gotthrow);
|
|
if (!gotsigfpe && !gotthrow) {
|
|
EXPECT_EQ(quotient, (uint64_t)Read64(m->ax));
|
|
EXPECT_EQ(remainder, (uint64_t)Read64(m->dx));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BENCH(imul, bench) {
|
|
volatile register int8_t x8, y8;
|
|
volatile register int16_t x16, y16;
|
|
volatile register int32_t x32, y32;
|
|
volatile register int64_t x64, y64;
|
|
EZBENCH2("imul8", PROGN(x8 = 7, y8 = 18), y8 *= x8);
|
|
EZBENCH2("imul16", PROGN(x16 = 123, y16 = 116), y16 *= x16);
|
|
EZBENCH2("imul32", PROGN(x32 = 0x238943, y32 = 0x238), y32 *= x32);
|
|
EZBENCH2("imul64", PROGN(x64 = 0x23894329838932, y64 = 0x238), y64 *= x64);
|
|
}
|
|
|
|
BENCH(idiv, bench) {
|
|
volatile register int8_t x8, y8;
|
|
volatile register int16_t x16, y16;
|
|
volatile register int32_t x32, y32;
|
|
volatile register int64_t x64, y64;
|
|
EZBENCH2("idiv8", PROGN(x8 = 7, y8 = 18), x8 /= y8);
|
|
EZBENCH2("idiv16", PROGN(x16 = 123, y16 = 116), x16 /= y16);
|
|
EZBENCH2("idiv32", PROGN(x32 = 0x238943298, y32 = 0x238), x32 /= y32);
|
|
EZBENCH2("idiv64", PROGN(x64 = 0x23894329838932, y64 = 0x238), x64 /= y64);
|
|
}
|
|
|
|
BENCH(mul, bench) {
|
|
volatile register uint8_t x8, y8;
|
|
volatile register uint16_t x16, y16;
|
|
volatile register uint32_t x32, y32;
|
|
volatile register uint64_t x64, y64;
|
|
EZBENCH2("mul8", PROGN(x8 = 7, y8 = 18), y8 *= x8);
|
|
EZBENCH2("mul16", PROGN(x16 = 123, y16 = 116), y16 *= x16);
|
|
EZBENCH2("mul32", PROGN(x32 = 0x238943, y32 = 0x238), y32 *= x32);
|
|
EZBENCH2("mul64", PROGN(x64 = 0x23894329838932, y64 = 0x238), y64 *= x64);
|
|
}
|
|
|
|
BENCH(div, bench) {
|
|
volatile register uint8_t x8, y8;
|
|
volatile register uint16_t x16, y16;
|
|
volatile register uint32_t x32, y32;
|
|
volatile register uint64_t x64, y64;
|
|
EZBENCH2("div8", PROGN(x8 = 7, y8 = 18), x8 /= y8);
|
|
EZBENCH2("div16", PROGN(x16 = 123, y16 = 116), x16 /= y16);
|
|
EZBENCH2("div32", PROGN(x32 = 0x238943298, y32 = 0x238), x32 /= y32);
|
|
EZBENCH2("div64", PROGN(x64 = 0x23894329838932, y64 = 0x238), x64 /= y64);
|
|
}
|