325 lines
10 KiB
C
325 lines
10 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/macros.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/testlib/testlib.h"
|
|
#include "test/tool/build/lib/numbers.h"
|
|
#include "test/tool/build/lib/optest.h"
|
|
#include "tool/build/lib/alu.h"
|
|
#include "tool/build/lib/flags.h"
|
|
#include "tool/build/lib/machine.h"
|
|
|
|
#define NATIVE_ALU2(MODE, INSTRUCTION) \
|
|
asm("pushf\n\t" \
|
|
"andl\t%3,(%%rsp)\n\t" \
|
|
"orl\t%4,(%%rsp)\n\t" \
|
|
"popf\n\t" INSTRUCTION "\t%b2,%" MODE "0\n\t" \
|
|
"pushf\n\t" \
|
|
"pop\t%q1" \
|
|
: "+r"(x), "=r"(*f) \
|
|
: "c"(y), "i"(~FMASK), "r"(*f & FMASK) \
|
|
: "cc")
|
|
|
|
#define NATIVE_ALU2_ANYBITS(INSTRUCTION, MUTATING) \
|
|
switch (w) { \
|
|
case 0: \
|
|
NATIVE_ALU2("b", INSTRUCTION); \
|
|
if (MUTATING) x &= 0xff; \
|
|
return x; \
|
|
case 1: \
|
|
NATIVE_ALU2("w", INSTRUCTION); \
|
|
if (MUTATING) x &= 0xffff; \
|
|
return x; \
|
|
case 2: \
|
|
NATIVE_ALU2("k", INSTRUCTION); \
|
|
return x; \
|
|
case 3: \
|
|
NATIVE_ALU2("q", INSTRUCTION); \
|
|
return x; \
|
|
default: \
|
|
abort(); \
|
|
}
|
|
|
|
int64_t Bsu(int w, int h, uint64_t x, uint64_t y, uint32_t *f) {
|
|
switch (h & 7) {
|
|
case BSU_SHR:
|
|
switch (w) {
|
|
case 0:
|
|
return Shr8(x, y, f);
|
|
case 1:
|
|
return Shr16(x, y, f);
|
|
case 2:
|
|
return Shr32(x, y, f);
|
|
case 3:
|
|
return Shr64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_SAL:
|
|
case BSU_SHL:
|
|
switch (w) {
|
|
case 0:
|
|
return Shl8(x, y, f);
|
|
case 1:
|
|
return Shl16(x, y, f);
|
|
case 2:
|
|
return Shl32(x, y, f);
|
|
case 3:
|
|
return Shl64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_SAR:
|
|
switch (w) {
|
|
case 0:
|
|
return Sar8(x, y, f);
|
|
case 1:
|
|
return Sar16(x, y, f);
|
|
case 2:
|
|
return Sar32(x, y, f);
|
|
case 3:
|
|
return Sar64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_ROL:
|
|
switch (w) {
|
|
case 0:
|
|
return Rol8(x, y, f);
|
|
case 1:
|
|
return Rol16(x, y, f);
|
|
case 2:
|
|
return Rol32(x, y, f);
|
|
case 3:
|
|
return Rol64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_ROR:
|
|
switch (w) {
|
|
case 0:
|
|
return Ror8(x, y, f);
|
|
case 1:
|
|
return Ror16(x, y, f);
|
|
case 2:
|
|
return Ror32(x, y, f);
|
|
case 3:
|
|
return Ror64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_RCR:
|
|
switch (w) {
|
|
case 0:
|
|
return Rcr8(x, y, f);
|
|
case 1:
|
|
return Rcr16(x, y, f);
|
|
case 2:
|
|
return Rcr32(x, y, f);
|
|
case 3:
|
|
return Rcr64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
case BSU_RCL:
|
|
switch (w) {
|
|
case 0:
|
|
return Rcl8(x, y, f);
|
|
case 1:
|
|
return Rcl16(x, y, f);
|
|
case 2:
|
|
return Rcl32(x, y, f);
|
|
case 3:
|
|
return Rcl64(x, y, f);
|
|
default:
|
|
unreachable;
|
|
}
|
|
default:
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
int64_t RunGolden(char w, int h, uint64_t x, uint64_t y, uint32_t *f) {
|
|
switch (h & 7) {
|
|
case BSU_ROR:
|
|
NATIVE_ALU2_ANYBITS("ror", true);
|
|
case BSU_SHL:
|
|
NATIVE_ALU2_ANYBITS("shl", true);
|
|
case BSU_SAL:
|
|
NATIVE_ALU2_ANYBITS("sal", true);
|
|
case BSU_RCR:
|
|
NATIVE_ALU2_ANYBITS("rcr", true);
|
|
case BSU_SAR:
|
|
NATIVE_ALU2_ANYBITS("sar", true);
|
|
case BSU_SHR:
|
|
NATIVE_ALU2_ANYBITS("shr", true);
|
|
case BSU_RCL:
|
|
NATIVE_ALU2_ANYBITS("rcl", true);
|
|
case BSU_ROL:
|
|
NATIVE_ALU2_ANYBITS("rol", true);
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
const uint8_t kBsuOps[] = {
|
|
BSU_SHR, BSU_SHL, BSU_ROL, BSU_ROR, BSU_RCL,
|
|
BSU_RCR, BSU_SHL, BSU_SAL, BSU_SAR,
|
|
};
|
|
|
|
const char *const kBsuNames[] = {
|
|
[BSU_ROL] = "rol", [BSU_ROR] = "ror", [BSU_RCL] = "rcl", [BSU_RCR] = "rcr",
|
|
[BSU_SHL] = "shl", [BSU_SHR] = "shr", [BSU_SAL] = "sal", [BSU_SAR] = "sar",
|
|
};
|
|
|
|
int64_t RunOpTest(char w, int h, uint64_t x, uint64_t y, uint32_t *f) {
|
|
return Bsu(w, h, x, y, f);
|
|
}
|
|
|
|
void FixupUndefOpTestFlags(char w, int h, uint64_t x, uint64_t y,
|
|
uint32_t goldenflags, uint32_t *flags) {
|
|
y &= w == 3 ? 0x3F : 0x1F;
|
|
if (y != 0 && y != 1) {
|
|
*flags = SetFlag(*flags, FLAGS_OF, GetFlag(goldenflags, FLAGS_OF));
|
|
}
|
|
}
|
|
|
|
TEST(bsu, test) {
|
|
RunOpTests(kBsuOps, ARRAYLEN(kBsuOps), kBsuNames);
|
|
}
|
|
|
|
TEST(shrd32, smoke) {
|
|
int i, j, k;
|
|
uint8_t b, cf, of;
|
|
uint32_t x, y, z1, f, z2, unflag;
|
|
for (i = 0; i < ARRAYLEN(kNumbers); ++i) {
|
|
x = kNumbers[i];
|
|
for (j = 0; j < ARRAYLEN(kNumbers); ++j) {
|
|
y = kNumbers[j];
|
|
for (k = 0; k < ARRAYLEN(kNumbers); ++k) {
|
|
f = 0;
|
|
cf = 0;
|
|
of = 0;
|
|
b = kNumbers[k];
|
|
z1 = BsuDoubleShift(2, x, y, b, true, &f);
|
|
z2 = x;
|
|
asm("xor\t%1,%1\n\t"
|
|
"shrd\t%5,%4,%0"
|
|
: "+rm"(z2), "=&r"(unflag), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(y), "c"(b)
|
|
: "cc");
|
|
ASSERT_EQ(z2, z1);
|
|
ASSERT_EQ(cf, GetFlag(f, FLAGS_CF));
|
|
if (b <= 1) ASSERT_EQ(of, GetFlag(f, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(shld16, smoke) {
|
|
uint32_t f;
|
|
int i, j, k;
|
|
uint8_t b, cf, of;
|
|
uint16_t x, y, z1, z2, unflag;
|
|
for (i = 0; i < ARRAYLEN(kNumbers); ++i) {
|
|
x = kNumbers[i];
|
|
for (j = 0; j < ARRAYLEN(kNumbers); ++j) {
|
|
y = kNumbers[j];
|
|
for (k = 0; k < ARRAYLEN(kNumbers); ++k) {
|
|
f = 0;
|
|
cf = 0;
|
|
of = 0;
|
|
b = kNumbers[k];
|
|
if (b > 16) continue;
|
|
z1 = BsuDoubleShift(1, x, y, b, false, &f);
|
|
z2 = x;
|
|
asm("xor\t%1,%1\n\t"
|
|
"shldw\t%5,%4,%0"
|
|
: "+rm"(z2), "=&r"(unflag), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(y), "c"(b)
|
|
: "cc");
|
|
ASSERT_EQ(z2, z1);
|
|
ASSERT_EQ(cf, GetFlag(f, FLAGS_CF));
|
|
if (b <= 1) ASSERT_EQ(of, GetFlag(f, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(shld32, smoke) {
|
|
int i, j, k;
|
|
uint8_t b, cf, of;
|
|
uint32_t x, y, z1, f, z2, unflag;
|
|
for (i = 0; i < ARRAYLEN(kNumbers); ++i) {
|
|
x = kNumbers[i];
|
|
for (j = 0; j < ARRAYLEN(kNumbers); ++j) {
|
|
y = kNumbers[j];
|
|
for (k = 0; k < ARRAYLEN(kNumbers); ++k) {
|
|
f = 0;
|
|
cf = 0;
|
|
of = 0;
|
|
b = kNumbers[k];
|
|
if (b > 32) continue;
|
|
z1 = BsuDoubleShift(2, x, y, b, false, &f);
|
|
z2 = x;
|
|
asm("xor\t%1,%1\n\t"
|
|
"shld\t%5,%4,%0"
|
|
: "+rm"(z2), "=&r"(unflag), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(y), "c"(b)
|
|
: "cc");
|
|
ASSERT_EQ(z2, z1);
|
|
ASSERT_EQ(cf, GetFlag(f, FLAGS_CF));
|
|
if (b <= 1) ASSERT_EQ(of, GetFlag(f, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(shld64, smoke) {
|
|
uint32_t f;
|
|
int i, j, k;
|
|
uint8_t b, cf, of;
|
|
uint64_t x, y, z1, z2, unflag;
|
|
for (i = 0; i < ARRAYLEN(kNumbers); ++i) {
|
|
x = kNumbers[i];
|
|
for (j = 0; j < ARRAYLEN(kNumbers); ++j) {
|
|
y = kNumbers[j];
|
|
for (k = 0; k < ARRAYLEN(kNumbers); ++k) {
|
|
f = 0;
|
|
cf = 0;
|
|
of = 0;
|
|
b = kNumbers[k];
|
|
if (b > 64) continue;
|
|
z1 = BsuDoubleShift(3, x, y, b, false, &f);
|
|
z2 = x;
|
|
asm("xor\t%1,%1\n\t"
|
|
"shldq\t%5,%4,%0"
|
|
: "+rm"(z2), "=&r"(unflag), "=@ccc"(cf), "=@cco"(of)
|
|
: "r"(y), "c"(b)
|
|
: "cc");
|
|
ASSERT_EQ(z2, z1);
|
|
ASSERT_EQ(cf, GetFlag(f, FLAGS_CF));
|
|
if (b <= 1) ASSERT_EQ(of, GetFlag(f, FLAGS_OF));
|
|
}
|
|
}
|
|
}
|
|
}
|