122 lines
4.0 KiB
C
122 lines
4.0 KiB
C
/* flan_rt — the milestone-2 host ABI.
|
|
*
|
|
* This is the whole of it: argv, stdout, exit, and four text conversions
|
|
* (plan.org, Milestone-2 primitives). Keeping the list this short is what
|
|
* makes the wasm32 target cheap, because a primitive is the only thing
|
|
* implemented twice.
|
|
*
|
|
* Every function here takes and returns scalars or an out-pointer. Nothing
|
|
* returns a struct by value: the emitted .ll would then have to agree with the
|
|
* platform's struct-return ABI, which is exactly the kind of thing that works
|
|
* on x86-64 and silently does not on wasm32.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* [T] and string are both ptr+len — see Emit.ll. */
|
|
typedef struct { const uint8_t *ptr; int64_t len; } flan_slice;
|
|
|
|
static int rt_argc;
|
|
static char **rt_argv;
|
|
static flan_slice *rt_args; /* argv as [string], built once, never freed */
|
|
|
|
void flan_rt_init(int32_t argc, char **argv) {
|
|
rt_argc = (int)argc;
|
|
rt_argv = argv;
|
|
}
|
|
|
|
void flan_argv(flan_slice *out) {
|
|
if (rt_args == NULL && rt_argc > 0) {
|
|
rt_args = (flan_slice *)malloc(sizeof(flan_slice) * (size_t)rt_argc);
|
|
for (int i = 0; i < rt_argc; i++) {
|
|
rt_args[i].ptr = (const uint8_t *)rt_argv[i];
|
|
rt_args[i].len = (int64_t)strlen(rt_argv[i]);
|
|
}
|
|
}
|
|
out->ptr = (const uint8_t *)rt_args;
|
|
out->len = (int64_t)rt_argc;
|
|
}
|
|
|
|
void flan_write_stdout(const uint8_t *p, int64_t n) {
|
|
if (n > 0) fwrite(p, 1, (size_t)n, stdout);
|
|
}
|
|
|
|
void flan_exit(int32_t status) {
|
|
fflush(stdout);
|
|
exit((int)status);
|
|
}
|
|
|
|
/* The conversions are *text*: bytes->f64 parses "12.5", f64->bytes renders it.
|
|
* calc-me's tokenizer needs the first, the prelude's printers the second. */
|
|
|
|
#define SCRATCH 64
|
|
static char scratch[SCRATCH]; /* rendered text lives here until the next call */
|
|
|
|
double flan_bytes_to_f64(const uint8_t *p, int64_t n) {
|
|
char buf[512];
|
|
size_t k = (size_t)n < sizeof buf - 1 ? (size_t)n : sizeof buf - 1;
|
|
memcpy(buf, p, k);
|
|
buf[k] = '\0';
|
|
return strtod(buf, NULL);
|
|
}
|
|
|
|
int64_t flan_bytes_to_i64(const uint8_t *p, int64_t n) {
|
|
char buf[64];
|
|
size_t k = (size_t)n < sizeof buf - 1 ? (size_t)n : sizeof buf - 1;
|
|
memcpy(buf, p, k);
|
|
buf[k] = '\0';
|
|
return (int64_t)strtoll(buf, NULL, 10);
|
|
}
|
|
|
|
/* %g so that 3.5 prints as "3.5" and not "3.500000" — calc-me's expected
|
|
* output is a table of exact strings. */
|
|
void flan_f64_to_bytes(double x, flan_slice *out) {
|
|
int n = snprintf(scratch, SCRATCH, "%g", x);
|
|
out->ptr = (const uint8_t *)scratch;
|
|
out->len = n < 0 ? 0 : (int64_t)n;
|
|
}
|
|
|
|
void flan_i64_to_bytes(int64_t x, flan_slice *out) {
|
|
int n = snprintf(scratch, SCRATCH, "%lld", (long long)x);
|
|
out->ptr = (const uint8_t *)scratch;
|
|
out->len = n < 0 ? 0 : (int64_t)n;
|
|
}
|
|
|
|
/* Bounds failures. The emitted code branches here and then falls off the end
|
|
* with `unreachable`, so these must not return — the same explicit shape as
|
|
* every other non-local exit, which is what keeps wasm32 free of unwinding.
|
|
*
|
|
* The location is passed as ptr+len because that is what a Flan string already
|
|
* is; nothing here allocates. Exit 134 is abort()'s status without abort()'s
|
|
* signal, so the same assertion should hold once wasm32 builds.
|
|
*
|
|
* stdout is flushed *before* the message: stderr is unbuffered and a
|
|
* redirected stdout is not, so without this the error appears above the output
|
|
* that led to it. */
|
|
|
|
static _Noreturn void rt_die(void) {
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
exit(134);
|
|
}
|
|
|
|
_Noreturn void flan_bounds_fail(const uint8_t *loc, int64_t loclen,
|
|
int64_t idx, int64_t len) {
|
|
fflush(stdout);
|
|
fprintf(stderr, "%.*s: index %lld is out of bounds for length %lld\n",
|
|
(int)loclen, (const char *)loc, (long long)idx, (long long)len);
|
|
rt_die();
|
|
}
|
|
|
|
_Noreturn void flan_slice_fail(const uint8_t *loc, int64_t loclen,
|
|
int64_t lo, int64_t hi, int64_t len) {
|
|
fflush(stdout);
|
|
fprintf(stderr, "%.*s: slice [%lld %lld) is out of bounds for length %lld\n",
|
|
(int)loclen, (const char *)loc, (long long)lo, (long long)hi,
|
|
(long long)len);
|
|
rt_die();
|
|
}
|