Files
cosmopolitan/tool/viz/generatematrix.c
Justine Tunney f4f4caab0e Add x86_64-linux-gnu emulator
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
2020-08-25 04:43:42 -07:00

316 lines
9.6 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/safemacros.h"
#include "libc/conv/conv.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/rand/lcg.h"
#include "libc/rand/rand.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/x/x.h"
#include "third_party/dtoa/dtoa.h"
#include "third_party/getopt/getopt.h"
#include "tool/viz/lib/formatstringtable.h"
typedef double (*round_f)(double);
typedef unsigned long (*rand_f)(void);
struct Range {
long double a;
long double b;
};
short xn_ = 8;
short yn_ = 8;
double digs_ = 6;
rand_f rand_;
round_f rounder_;
const char *path_ = "-";
const char *name_ = "M";
const char *type_ = "float";
struct Range r1_ = {LONG_MIN, LONG_MAX};
struct Range r2_ = {0, 1};
StringTableFormatter *formatter_ = FormatStringTableAsCode;
static noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "Usage: %s%s", program_invocation_name, "\
[FLAGS] [FILE]\n\
\n\
Flags:\n\
-u unsigned\n\
-c char\n\
-s short\n\
-i int\n\
-l long\n\
-d double\n\
-b bytes [-uc]\n\
-g non-deterministic rng\n\
-S output assembly\n\
-W output whitespace\n\
-o PATH output path\n\
-x FLEX\n\
-w FLEX width\n\
-y FLEX\n\
-h FLEX height\n\
-N NAME name\n\
-T NAME type name\n\
-A FLEX min value\n\
-B FLEX max value\n\
-R FUNC round function for indexing\n\
-D FLEX decimal digits to printout\n\
-v increases verbosity\n\
-? shows this information\n\
\n");
exit(rc);
}
static bool StringEquals(const char *a, const char *b) {
return strcasecmp(a, b) == 0;
}
static noreturn void ShowInvalidArg(const char *name, const char *s,
const char *type) {
fprintf(stderr, "error: invalid %s %s: %s\n", type, name, s);
exit(EXIT_FAILURE);
}
static double ParseFlexidecimalOrDie(const char *name, const char *s,
double min, double max) {
double x;
s = firstnonnull(s, "NULL");
if (strchr(s, '.') || strchr(s, 'e')) {
x = strtod(s, NULL);
} else {
x = strtol(s, NULL, 0);
}
if (min <= x && x <= max) {
return x;
} else {
ShowInvalidArg(name, s, "flexidecimal");
}
}
static round_f ParseRoundingFunctionOrDie(const char *s) {
if (isempty(s) || StringEquals(s, "none") || StringEquals(s, "null")) {
return NULL;
} else if (StringEquals(s, "round")) {
return round;
} else if (StringEquals(s, "rint")) {
return rint;
} else if (StringEquals(s, "nearbyint")) {
return nearbyint;
} else if (StringEquals(s, "trunc")) {
return trunc;
} else if (StringEquals(s, "floor")) {
return floor;
} else if (StringEquals(s, "ceil")) {
return ceil;
} else {
ShowInvalidArg("round", s, "func");
}
}
static void ConfigureIntegralRange(const char *type, long min, long max) {
type_ = type;
r1_.a = min;
r1_.b = max;
r2_.a = min;
r2_.b = max;
if (!rounder_) rounder_ = round;
}
void GetOpts(int argc, char *argv[]) {
int opt;
bool want_unsigned, want_char, want_short, want_int, want_long, want_double;
want_unsigned = false;
want_char = false;
want_short = false;
want_int = false;
want_long = false;
want_double = false;
if (argc == 2 &&
(StringEquals(argv[1], "--help") || StringEquals(argv[1], "-help"))) {
PrintUsage(EXIT_SUCCESS, stdout);
}
while ((opt = getopt(argc, argv, "?vubcsildgSWo:x:w:y:h:N:A:B:C:E:T:R:D:")) !=
-1) {
switch (opt) {
case 'b':
want_unsigned = true;
want_char = true;
break;
case 'u':
want_unsigned = true;
break;
case 'c':
want_char = true;
break;
case 's':
want_short = true;
break;
case 'i':
want_int = true;
break;
case 'l':
want_long = true;
break;
case 'd':
want_double = true;
break;
case 'g':
rand_ = rand64;
break;
case 'N':
name_ = optarg;
break;
case 'o':
path_ = optarg;
break;
case 'S':
formatter_ = FormatStringTableAsAssembly;
break;
case 'W':
formatter_ = FormatStringTableBasic;
break;
case 't':
type_ = optarg;
break;
case 'x':
case 'w':
xn_ = ParseFlexidecimalOrDie("width", optarg, 1, SHRT_MAX);
break;
case 'y':
case 'h':
yn_ = ParseFlexidecimalOrDie("height", optarg, 1, SHRT_MAX);
break;
case 'D':
digs_ = ParseFlexidecimalOrDie("digs", optarg, 0, 15.95);
break;
case 'r':
rounder_ = ParseRoundingFunctionOrDie(optarg);
break;
case 'A':
r1_.a = ParseFlexidecimalOrDie("r1_.a", optarg, INT_MIN, INT_MAX);
break;
case 'B':
r1_.b = ParseFlexidecimalOrDie("r1_.b", optarg, INT_MIN, INT_MAX);
break;
case 'C':
r2_.a = ParseFlexidecimalOrDie("r2_.a", optarg, INT_MIN, INT_MAX);
break;
case 'E':
r2_.b = ParseFlexidecimalOrDie("r2_.b", optarg, INT_MIN, INT_MAX);
break;
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
if (want_unsigned && want_char) {
ConfigureIntegralRange("unsigned char", 0, 255);
} else if (want_char) {
ConfigureIntegralRange("signed char", -128, 127);
} else if (want_unsigned && want_short) {
ConfigureIntegralRange("unsigned short", USHRT_MIN, USHRT_MAX);
} else if (want_short) {
ConfigureIntegralRange("short", SHRT_MIN, SHRT_MAX);
} else if (want_unsigned && want_int) {
ConfigureIntegralRange("unsigned", UINT_MIN, UINT_MAX);
} else if (want_int) {
ConfigureIntegralRange("int", INT_MIN, INT_MAX);
} else if (want_unsigned && want_long) {
ConfigureIntegralRange("unsigned long", ULONG_MIN, ULONG_MAX);
} else if (want_long) {
ConfigureIntegralRange("long", LONG_MIN, LONG_MAX);
} else if (want_double) {
type_ = "double";
r1_.a = LONG_MIN;
r1_.b = LONG_MAX;
digs_ = 19;
}
}
static void *SetRandom(long n, long p[n]) {
long i;
uint64_t r;
if (rand_) {
for (r = 1, i = 0; i < n; ++i) {
p[i] = rand_();
}
} else {
for (r = 1, i = 0; i < n; ++i) {
p[i] = KnuthLinearCongruentialGenerator(&r) >> 32 |
KnuthLinearCongruentialGenerator(&r) >> 32 << 32;
}
}
return p;
}
static long double ConvertRange(long double x, long double a, long double b,
long double c, long double d) {
return (d - c) / (b - a) * (x - a) + c;
}
static double Compand(long x, double a, double b, double c, double d) {
return ConvertRange(ConvertRange(x, LONG_MIN, LONG_MAX, a, b), a, b, c, d);
}
static void GenerateMatrixImpl(long I[yn_][xn_], double M[yn_][xn_], FILE *f) {
long y, x;
for (y = 0; y < yn_; ++y) {
for (x = 0; x < xn_; ++x) {
M[y][x] = Compand(I[y][x], r1_.a, r1_.b, r2_.a, r2_.b);
}
if (rounder_) {
for (x = 0; x < xn_; ++x) {
M[y][x] = rounder_(M[y][x]);
}
}
}
FormatMatrixDouble(yn_, xn_, M, fputs, f, formatter_, type_, name_, NULL,
digs_, round);
}
void GenerateMatrix(FILE *f) {
GenerateMatrixImpl(SetRandom(yn_ * xn_, gc(calloc(yn_ * xn_, sizeof(long)))),
gc(calloc(yn_ * xn_, sizeof(double))), f);
}
int main(int argc, char *argv[]) {
int i;
FILE *f;
showcrashreports();
GetOpts(argc, argv);
CHECK_NOTNULL((f = fopen(path_, "w")));
if (optind < argc) FATALF("TODO(jart): support input files");
GenerateMatrix(f);
return fclose(f);
}