do not edit — generated by btf.
git.druid.rocksindexdruid520forthcsrc/forthc.c

src/forthc.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
 
#define BASE 0x100000L
#define STACKBASE 0x400000L
#define EHSIZE 64L
#define PHSIZE 56L
#define NOTESIZE 24L
#define SCRATCH_SIZE 8L
#define LOOP_MAX 32L
#define LOOPAREA_SIZE (LOOP_MAX * 16L)
#define DSTACK_SIZE (8192L * 8L)
 
#define TOK_LEN 128
#define TOK_MAX 65536
#define DICT_MAX 8192
#define CTRL_MAX 1024
 
#define RAX 0
#define RCX 1
#define RDX 2
#define RBX 3
#define RSP 4
#define RBP 5
#define RSI 6
#define RDI 7
#define R8  8
#define R9  9
#define R15 15
 
#define CC_E  4
#define CC_NE 5
#define CC_S  8
#define CC_NS 9
#define CC_L  12
#define CC_GE 13
#define CC_LE 14
#define CC_G  15
 
#define G1_ADD 0
#define G1_SUB 5
 
#define F7_NOT  2
#define F7_NEG  3
#define F7_DIV  6
#define F7_IDIV 7
 
#define OP_ADD 1
#define OP_SUB 2
#define OP_MUL 3
#define OP_DIV 4
#define OP_MOD 5
#define OP_DUP 6
#define OP_DROP 7
#define OP_SWAP 8
#define OP_OVER 9
#define OP_ROT 10
#define OP_EQ 11
#define OP_NE 12
#define OP_LT 13
#define OP_GT 14
#define OP_LE 15
#define OP_GE 16
#define OP_ZEQ 17
#define OP_ZLT 18
#define OP_AND 19
#define OP_OR 20
#define OP_XOR 21
#define OP_INVERT 22
#define OP_NEG 23
#define OP_INC 24
#define OP_DEC 25
#define OP_FETCH 26
#define OP_STORE 27
#define OP_PLUSSTORE 28
#define OP_EMIT 29
#define OP_DOT 30
#define OP_CR 31
#define OP_SPACE 32
#define OP_TRUE 33
#define OP_FALSE 34
#define OP_BYE 35
#define OP_NIP 36
 
#define CK_IF 1
#define CK_ELSE 2
#define CK_BEGIN 3
#define CK_WHILE 4
#define CK_DO 5
#define CK_COLON 6
 
#define DK_WORD 0
#define DK_VAR 1
#define DK_CONST 2
 
#define OS_LINUX 0
#define OS_NETBSD 1
 
typedef struct
{
	unsigned char* data;
	long len;
	long cap;
} buf;
 
typedef struct
{
	char name[32];
	int kind;
	long val;
} dent;
 
typedef struct
{
	int kind;
	long a;
	long b;
} cent;
 
typedef struct
{
	const char* name;
	int op;
} primword;
 
static primword PRIMS[] =
{
	{"+", OP_ADD}, {"-", OP_SUB}, {"*", OP_MUL}, {"/", OP_DIV}, {"mod", OP_MOD},
	{"dup", OP_DUP}, {"drop", OP_DROP}, {"swap", OP_SWAP}, {"over", OP_OVER}, {"rot", OP_ROT},
	{"=", OP_EQ}, {"<>", OP_NE}, {"<", OP_LT}, {">", OP_GT}, {"<=", OP_LE}, {">=", OP_GE},
	{"0=", OP_ZEQ}, {"0<", OP_ZLT},
	{"and", OP_AND}, {"or", OP_OR}, {"xor", OP_XOR}, {"invert", OP_INVERT}, {"negate", OP_NEG},
	{"1+", OP_INC}, {"1-", OP_DEC},
	{"@", OP_FETCH}, {"!", OP_STORE}, {"+!", OP_PLUSSTORE},
	{"emit", OP_EMIT}, {".", OP_DOT}, {"cr", OP_CR}, {"space", OP_SPACE},
	{"true", OP_TRUE}, {"false", OP_FALSE}, {"bye", OP_BYE},
	{"nip", OP_NIP},
	{NULL, 0}
};
 
static dent dict[DICT_MAX];
static long ndict = 0;
 
static cent cstack[CTRL_MAX];
static long ctop = 0;
 
static char toks[TOK_MAX][TOK_LEN];
static long toklines[TOK_MAX];
static int tokisstr[TOK_MAX];
static long ntok = 0;
 
static long scratch_addr = 0;
static long looparea_addr = 0;
static long vars_addr = 0;
static long dstack_base = 0;
static long dstack_top = 0;
static long codebase = 0;
static long printnum_addr = 0;
 
static long varcount = 0;
static long loopdepth = 0;
static int compiling = 0;
static long curwordaddr = 0;
 
static long targetos = OS_LINUX;
static long sys_write = 1;
static long sys_exit = 60;
 
static void fail(const char* msg);
static void failat(long line, const char* msg, const char* tok);
static char* readall(FILE* f, long* outlen);
static void tokenize(const char* src, long srclen);
static int isnum(const char* s, long* out);
 
static long dictfind(const char* name);
static void dictadd(const char* name, int kind, long val);
static void cpush(int kind, long a, long b);
static cent cpop(void);
 
static void bufinit(buf* c);
static void bufneed(buf* c, long more);
static void bufbyte(buf* c, long v);
static void bufu16(buf* c, long v);
static void bufu32(buf* c, long v);
static void bufu64(buf* c, long v);
static void bufpatch32(buf* c, long pos, long v);
static void bufzero(buf* c, long n);
static void bufappend(buf* c, unsigned char* p, long n);
static long here(buf* c);
 
static void rex(buf* c, int w, int reg, int rm);
static void modrm(buf* c, int mod, int reg, int rm);
static void memop(buf* c, int reg, int base, long disp);
static void i_movrm(buf* c, int dst, int base, long disp);
static void i_movmr(buf* c, int base, long disp, int src);
static void i_movmr8(buf* c, int base, long disp, int src);
static void i_movmi8(buf* c, int base, long disp, int val);
static void i_movrr(buf* c, int dst, int src);
static void i_movi32z(buf* c, int dst, unsigned long val);
static void i_movi64(buf* c, int dst, long val);
static void i_lea(buf* c, int dst, int base, long disp);
static void i_alurr(buf* c, int opcode, int reg, int rm);
static void i_imulrr(buf* c, int dst, int src);
static void i_testrr(buf* c, int reg, int rm);
static void i_g1i8(buf* c, int ext, int rm, int imm8);
static void i_f7(buf* c, int ext, int rm);
static void i_cqo(buf* c);
static void i_ret(buf* c);
static void i_syscall(buf* c);
static long i_jccph(buf* c, int cc);
static long i_jmpph(buf* c);
static void i_patchhere(buf* c, long pos);
static void i_jccto(buf* c, int cc, long target);
static void i_jmpto(buf* c, long target);
static void i_callto(buf* c, long target);
 
static void gen_lit(buf* c, long v);
static void gen_dup(buf* c);
static void gen_drop(buf* c);
static void gen_swap(buf* c);
static void gen_over(buf* c);
static void gen_rot(buf* c);
static void gen_add(buf* c);
static void gen_sub(buf* c);
static void gen_mul(buf* c);
static void gen_divmod(buf* c, int ismod);
static void gen_cmp(buf* c, int skipcc);
static void gen_unarytest(buf* c, int skipcc);
static void gen_bitwise(buf* c, int opcode);
static void gen_unaryf7(buf* c, int ext);
static void gen_incdec(buf* c, int ext);
static void gen_fetch(buf* c);
static void gen_store(buf* c);
static void gen_plusstore(buf* c);
static void gen_emitcharimm(buf* c, int ch);
static void gen_emit(buf* c);
static void gen_dot(buf* c);
static void gen_dotquote(buf* c, const char* text, long len);
static void gen_bye(buf* c);
static void gen_printnum(buf* c);
 
static long loopidxaddr(long depth);
static long looplimaddr(long depth);
static void do_if(buf* c);
static void do_else(buf* c);
static void do_then(buf* c);
static void do_begin(buf* c);
static void do_until(buf* c);
static void do_while(buf* c);
static void do_repeat(buf* c);
static void do_do(buf* c);
static void do_i(buf* c);
static void do_loop(buf* c);
static void do_colon(buf* c, const char* name);
static void do_semi(buf* c);
static void do_recurse(buf* c);
static void do_variable(const char* name);
static void do_constant(const char* name, long val);
 
static long opfind(const char* name);
static void genop(buf* c, long idx);
static void compile(buf* c);
static long osphnum(long os);
static long osnotesize(long os);
static void emitelf(buf* o, long entry, long totalsize, long os);
static void emitnote(buf* o);
int main(int argc, char** argv);
 
static void
fail(const char* msg)
{
	fprintf(stderr, "err: %s\n", msg);
	exit(1);
}
 
static void
failat(long line, const char* msg, const char* tok)
{
	fprintf(stderr, "err: line %ld: %s '%s'.\n", line, msg, tok);
	exit(1);
}
 
static char*
readall(FILE* f, long* outlen)
{
	long cap;
	long len;
	char* b;
	long n;
 
	cap = 65536;
	len = 0;
	b = (char*)malloc((size_t)cap);
	if(b == NULL)
	{
		fail("out of memory.");
	}
	for(;;)
	{
		if(len + 4096 > cap)
		{
			cap = cap * 2;
			b = (char*)realloc(b, (size_t)cap);
			if(b == NULL)
			{
				fail("out of memory.");
			}
		}
		n = (long)fread(b + len, 1, 4096, f);
		len = len + n;
		if(n < 4096)
		{
			break;
		}
	}
	*outlen = len;
	return b;
}
 
static void
tokenize(const char* src, long srclen)
{
	long i;
	long line;
	long j;
 
	i = 0;
	line = 1;
	while(i < srclen)
	{
		if(src[i] == '\n')
		{
			line = line + 1;
			i = i + 1;
			continue;
		}
		if(src[i] == ' ' || src[i] == '\t' || src[i] == '\r')
		{
			i = i + 1;
			continue;
		}
		if(src[i] == '\\')
		{
			while(i < srclen && src[i] != '\n')
			{
				i = i + 1;
			}
			continue;
		}
		if(src[i] == '(')
		{
			while(i < srclen && src[i] != ')')
			{
				if(src[i] == '\n')
				{
					line = line + 1;
				}
				i = i + 1;
			}
			if(i < srclen)
			{
				i = i + 1;
			}
			continue;
		}
		if(src[i] == '.' && i + 1 < srclen && src[i + 1] == '"')
		{
			if(ntok == TOK_MAX)
			{
				fail("too many tokens.");
			}
			i = i + 2;
			if(i < srclen && src[i] == ' ')
			{
				i = i + 1;
			}
			toklines[ntok] = line;
			tokisstr[ntok] = 1;
			j = 0;
			while(i < srclen && src[i] != '"')
			{
				if(src[i] == '\n')
				{
					line = line + 1;
				}
				if(j == TOK_LEN - 1)
				{
					fail("string literal too long.");
				}
				toks[ntok][j] = src[i];
				j = j + 1;
				i = i + 1;
			}
			if(i >= srclen)
			{
				fail("unterminated string literal.");
			}
			i = i + 1;
			toks[ntok][j] = '\0';
			ntok = ntok + 1;
			continue;
		}
		if(ntok == TOK_MAX)
		{
			fail("too many tokens.");
		}
		toklines[ntok] = line;
		j = 0;
		while(i < srclen && src[i] != ' ' && src[i] != '\t' && src[i] != '\r' && src[i] != '\n')
		{
			if(j == TOK_LEN - 1)
			{
				fail("token too long.");
			}
			toks[ntok][j] = src[i];
			j = j + 1;
			i = i + 1;
		}
		toks[ntok][j] = '\0';
		ntok = ntok + 1;
	}
}
 
static int
isnum(const char* s, long* out)
{
	long i;
	long neg;
	long val;
	long got;
 
	i = 0;
	neg = 0;
	val = 0;
	got = 0;
	if(s[0] == '-')
	{
		neg = 1;
		i = 1;
	}
	while(s[i] != '\0')
	{
		if(s[i] < '0' || s[i] > '9')
		{
			return 0;
		}
		val = (val * 10) + (s[i] - '0');
		got = 1;
		i = i + 1;
	}
	if(got == 0)
	{
		return 0;
	}
	if(neg != 0)
	{
		*out = -val;
	}
	else
	{
		*out = val;
	}
	return 1;
}
 
static long
dictfind(const char* name)
{
	long i;
 
	for(i = ndict - 1; i >= 0; i = i - 1)
	{
		if(strcmp(dict[i].name, name) == 0)
		{
			return i;
		}
	}
	return -1;
}
 
static void
dictadd(const char* name, int kind, long val)
{
	if(ndict == DICT_MAX)
	{
		fail("too many words.");
	}
	if(strlen(name) >= 32)
	{
		fail("word name too long.");
	}
	strcpy(dict[ndict].name, name);
	dict[ndict].kind = kind;
	dict[ndict].val = val;
	ndict = ndict + 1;
}
 
static void
cpush(int kind, long a, long b)
{
	if(ctop == CTRL_MAX)
	{
		fail("control stack overflow.");
	}
	cstack[ctop].kind = kind;
	cstack[ctop].a = a;
	cstack[ctop].b = b;
	ctop = ctop + 1;
}
 
static cent
cpop(void)
{
	if(ctop == 0)
	{
		fail("unbalanced control structure.");
	}
	ctop = ctop - 1;
	return cstack[ctop];
}
 
static void
bufinit(buf* c)
{
	c->cap = 4096;
	c->len = 0;
	c->data = (unsigned char*)malloc((size_t)c->cap);
	if(c->data == NULL)
	{
		fail("out of memory.");
	}
}
 
static void
bufneed(buf* c, long more)
{
	long newcap;
	unsigned char* p;
 
	if(c->len + more <= c->cap)
	{
		return;
	}
	newcap = (c->cap * 2) + more + 256;
	p = (unsigned char*)malloc((size_t)newcap);
	if(p == NULL)
	{
		fail("out of memory.");
	}
	memcpy(p, c->data, (size_t)c->len);
	free(c->data);
	c->data = p;
	c->cap = newcap;
}
 
static void
bufbyte(buf* c, long v)
{
	bufneed(c, 1);
	c->data[c->len] = (unsigned char)(v & 0xFF);
	c->len = c->len + 1;
}
 
static void
bufu16(buf* c, long v)
{
	bufbyte(c, v);
	bufbyte(c, v >> 8);
}
 
static void
bufu32(buf* c, long v)
{
	bufbyte(c, v);
	bufbyte(c, v >> 8);
	bufbyte(c, v >> 16);
	bufbyte(c, v >> 24);
}
 
static void
bufu64(buf* c, long v)
{
	bufbyte(c, v);
	bufbyte(c, v >> 8);
	bufbyte(c, v >> 16);
	bufbyte(c, v >> 24);
	bufbyte(c, v >> 32);
	bufbyte(c, v >> 40);
	bufbyte(c, v >> 48);
	bufbyte(c, v >> 56);
}
 
static void
bufpatch32(buf* c, long pos, long v)
{
	c->data[pos] = (unsigned char)(v & 0xFF);
	c->data[pos + 1] = (unsigned char)((v >> 8) & 0xFF);
	c->data[pos + 2] = (unsigned char)((v >> 16) & 0xFF);
	c->data[pos + 3] = (unsigned char)((v >> 24) & 0xFF);
}
 
static void
bufzero(buf* c, long n)
{
	long i;
 
	for(i = 0; i < n; i = i + 1)
	{
		bufbyte(c, 0);
	}
}
 
static void
bufappend(buf* c, unsigned char* p, long n)
{
	long i;
 
	for(i = 0; i < n; i = i + 1)
	{
		bufbyte(c, p[i]);
	}
}
 
static long
here(buf* c)
{
	return codebase + c->len;
}
 
static void
rex(buf* c, int w, int reg, int rm)
{
	int r;
	int b;
 
	r = 0;
	if(reg >= 8)
	{
		r = 1;
	}
	b = 0;
	if(rm >= 8)
	{
		b = 1;
	}
	if(w != 0 || r != 0 || b != 0)
	{
		bufbyte(c, 0x40 | (w << 3) | (r << 2) | b);
	}
}
 
static void
modrm(buf* c, int mod, int reg, int rm)
{
	bufbyte(c, (mod << 6) | ((reg & 7) << 3) | (rm & 7));
}
 
static void
memop(buf* c, int reg, int base, long disp)
{
	int bl;
 
	bl = base & 7;
	if(disp == 0 && bl != 5)
	{
		modrm(c, 0, reg, base);
		if(bl == 4)
		{
			bufbyte(c, 0x24);
		}
	}
	else if(disp >= -128 && disp <= 127)
	{
		modrm(c, 1, reg, base);
		if(bl == 4)
		{
			bufbyte(c, 0x24);
		}
		bufbyte(c, disp);
	}
	else
	{
		modrm(c, 2, reg, base);
		if(bl == 4)
		{
			bufbyte(c, 0x24);
		}
		bufu32(c, disp);
	}
}
 
static void
i_movrm(buf* c, int dst, int base, long disp)
{
	rex(c, 1, dst, base);
	bufbyte(c, 0x8B);
	memop(c, dst, base, disp);
}
 
static void
i_movmr(buf* c, int base, long disp, int src)
{
	rex(c, 1, src, base);
	bufbyte(c, 0x89);
	memop(c, src, base, disp);
}
 
static void
i_movmr8(buf* c, int base, long disp, int src)
{
	rex(c, 0, src, base);
	bufbyte(c, 0x88);
	memop(c, src, base, disp);
}
 
static void
i_movmi8(buf* c, int base, long disp, int val)
{
	rex(c, 0, 0, base);
	bufbyte(c, 0xC6);
	memop(c, 0, base, disp);
	bufbyte(c, val);
}
 
static void
i_movrr(buf* c, int dst, int src)
{
	rex(c, 1, src, dst);
	bufbyte(c, 0x89);
	modrm(c, 3, src, dst);
}
 
static void
i_movi32z(buf* c, int dst, unsigned long val)
{
	rex(c, 0, 0, dst);
	bufbyte(c, 0xB8 | (dst & 7));
	bufu32(c, (long)val);
}
 
static void
i_movi64(buf* c, int dst, long val)
{
	rex(c, 1, 0, dst);
	bufbyte(c, 0xB8 | (dst & 7));
	bufu64(c, val);
}
 
static void
i_lea(buf* c, int dst, int base, long disp)
{
	rex(c, 1, dst, base);
	bufbyte(c, 0x8D);
	memop(c, dst, base, disp);
}
 
static void
i_alurr(buf* c, int opcode, int reg, int rm)
{
	rex(c, 1, reg, rm);
	bufbyte(c, opcode);
	modrm(c, 3, reg, rm);
}
 
static void
i_imulrr(buf* c, int dst, int src)
{
	rex(c, 1, dst, src);
	bufbyte(c, 0x0F);
	bufbyte(c, 0xAF);
	modrm(c, 3, dst, src);
}
 
static void
i_testrr(buf* c, int reg, int rm)
{
	rex(c, 1, reg, rm);
	bufbyte(c, 0x85);
	modrm(c, 3, reg, rm);
}
 
static void
i_g1i8(buf* c, int ext, int rm, int imm8)
{
	rex(c, 1, 0, rm);
	bufbyte(c, 0x83);
	modrm(c, 3, ext, rm);
	bufbyte(c, imm8);
}
 
static void
i_f7(buf* c, int ext, int rm)
{
	rex(c, 1, 0, rm);
	bufbyte(c, 0xF7);
	modrm(c, 3, ext, rm);
}
 
static void
i_cqo(buf* c)
{
	bufbyte(c, 0x48);
	bufbyte(c, 0x99);
}
 
static void
i_ret(buf* c)
{
	bufbyte(c, 0xC3);
}
 
static void
i_syscall(buf* c)
{
	bufbyte(c, 0x0F);
	bufbyte(c, 0x05);
}
 
static long
i_jccph(buf* c, int cc)
{
	long pos;
 
	bufbyte(c, 0x0F);
	bufbyte(c, 0x80 | cc);
	pos = c->len;
	bufu32(c, 0);
	return pos;
}
 
static long
i_jmpph(buf* c)
{
	long pos;
 
	bufbyte(c, 0xE9);
	pos = c->len;
	bufu32(c, 0);
	return pos;
}
 
static void
i_patchhere(buf* c, long pos)
{
	bufpatch32(c, pos, c->len - (pos + 4));
}
 
static void
i_jccto(buf* c, int cc, long target)
{
	long pos;
 
	bufbyte(c, 0x0F);
	bufbyte(c, 0x80 | cc);
	pos = c->len;
	bufu32(c, 0);
	bufpatch32(c, pos, target - (codebase + pos + 4));
}
 
static void
i_jmpto(buf* c, long target)
{
	long pos;
 
	bufbyte(c, 0xE9);
	pos = c->len;
	bufu32(c, 0);
	bufpatch32(c, pos, target - (codebase + pos + 4));
}
 
static void
i_callto(buf* c, long target)
{
	long pos;
 
	bufbyte(c, 0xE8);
	pos = c->len;
	bufu32(c, 0);
	bufpatch32(c, pos, target - (codebase + pos + 4));
}
 
static void
gen_lit(buf* c, long v)
{
	i_movi64(c, RAX, v);
	i_g1i8(c, G1_SUB, R15, 8);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_dup(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_SUB, R15, 8);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_drop(buf* c)
{
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_swap(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_movmr(c, R15, 0, RBX);
	i_movmr(c, R15, 8, RAX);
}
 
static void
gen_over(buf* c)
{
	i_movrm(c, RAX, R15, 8);
	i_g1i8(c, G1_SUB, R15, 8);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_rot(buf* c)
{
	i_movrm(c, RAX, R15, 16);
	i_movrm(c, RBX, R15, 8);
	i_movrm(c, RCX, R15, 0);
	i_movmr(c, R15, 16, RBX);
	i_movmr(c, R15, 8, RCX);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_add(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_alurr(c, 0x01, RAX, RBX);
	i_movmr(c, R15, 8, RBX);
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_sub(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_alurr(c, 0x29, RAX, RBX);
	i_movmr(c, R15, 8, RBX);
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_mul(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_imulrr(c, RBX, RAX);
	i_movmr(c, R15, 8, RBX);
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_divmod(buf* c, int ismod)
{
	i_movrm(c, RAX, R15, 8);
	i_movrm(c, RBX, R15, 0);
	i_cqo(c);
	i_f7(c, F7_IDIV, RBX);
	if(ismod != 0)
	{
		i_movmr(c, R15, 8, RDX);
	}
	else
	{
		i_movmr(c, R15, 8, RAX);
	}
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_cmp(buf* c, int skipcc)
{
	long p;
 
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_alurr(c, 0x39, RAX, RBX);
	i_movi32z(c, RAX, 0);
	p = i_jccph(c, skipcc);
	i_movi32z(c, RAX, 1);
	i_patchhere(c, p);
	i_movmr(c, R15, 8, RAX);
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_unarytest(buf* c, int skipcc)
{
	long p;
 
	i_movrm(c, RAX, R15, 0);
	i_testrr(c, RAX, RAX);
	i_movi32z(c, RCX, 0);
	p = i_jccph(c, skipcc);
	i_movi32z(c, RCX, 1);
	i_patchhere(c, p);
	i_movmr(c, R15, 0, RCX);
}
 
static void
gen_bitwise(buf* c, int opcode)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_alurr(c, opcode, RAX, RBX);
	i_movmr(c, R15, 8, RBX);
	i_g1i8(c, G1_ADD, R15, 8);
}
 
static void
gen_unaryf7(buf* c, int ext)
{
	i_movrm(c, RAX, R15, 0);
	i_f7(c, ext, RAX);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_incdec(buf* c, int ext)
{
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, ext, RAX, 1);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_fetch(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RAX, RAX, 0);
	i_movmr(c, R15, 0, RAX);
}
 
static void
gen_store(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_movmr(c, RAX, 0, RBX);
	i_g1i8(c, G1_ADD, R15, 16);
}
 
static void
gen_plusstore(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_movrm(c, RCX, RAX, 0);
	i_alurr(c, 0x01, RBX, RCX);
	i_movmr(c, RAX, 0, RCX);
	i_g1i8(c, G1_ADD, R15, 16);
}
 
static void
gen_emitcharimm(buf* c, int ch)
{
	i_movi32z(c, RCX, (unsigned long)scratch_addr);
	i_movmi8(c, RCX, 0, ch);
	i_movi32z(c, RAX, (unsigned long)sys_write);
	i_movi32z(c, RDI, 1);
	i_movi32z(c, RSI, (unsigned long)scratch_addr);
	i_movi32z(c, RDX, 1);
	i_syscall(c);
}
 
static void
gen_emit(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_ADD, R15, 8);
	i_movi32z(c, RCX, (unsigned long)scratch_addr);
	i_movmr8(c, RCX, 0, RAX);
	i_movi32z(c, RAX, (unsigned long)sys_write);
	i_movi32z(c, RDI, 1);
	i_movi32z(c, RSI, (unsigned long)scratch_addr);
	i_movi32z(c, RDX, 1);
	i_syscall(c);
}
 
static void
gen_dot(buf* c)
{
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_ADD, R15, 8);
	i_callto(c, printnum_addr);
}
 
static void
gen_dotquote(buf* c, const char* text, long len)
{
	long p;
	long straddr;
 
	p = i_jmpph(c);
	straddr = here(c);
	bufappend(c, (unsigned char*)text, len);
	i_patchhere(c, p);
	i_movi32z(c, RAX, (unsigned long)sys_write);
	i_movi32z(c, RDI, 1);
	i_movi32z(c, RSI, (unsigned long)straddr);
	i_movi32z(c, RDX, (unsigned long)len);
	i_syscall(c);
}
 
static void
gen_bye(buf* c)
{
	i_movi32z(c, RAX, (unsigned long)sys_exit);
	i_movi32z(c, RDI, 0);
	i_syscall(c);
}
 
static void
gen_printnum(buf* c)
{
	long jsign;
	long jnz;
	long jafter0;
	long jlex;
	long jneg;
	long looptarget;
 
	i_g1i8(c, G1_SUB, RSP, 32);
	i_movi32z(c, R9, 0);
	i_testrr(c, RAX, RAX);
	jsign = i_jccph(c, CC_NS);
	i_movi32z(c, R9, 1);
	i_f7(c, F7_NEG, RAX);
	i_patchhere(c, jsign);
	i_lea(c, RDI, RSP, 31);
	i_movi32z(c, RCX, 0);
	i_movi32z(c, RBX, 10);
	i_testrr(c, RAX, RAX);
	jnz = i_jccph(c, CC_NE);
	i_g1i8(c, G1_SUB, RDI, 1);
	i_movmi8(c, RDI, 0, '0');
	i_g1i8(c, G1_ADD, RCX, 1);
	jafter0 = i_jmpph(c);
	i_patchhere(c, jnz);
	looptarget = here(c);
	i_testrr(c, RAX, RAX);
	jlex = i_jccph(c, CC_E);
	i_movi32z(c, RDX, 0);
	i_f7(c, F7_DIV, RBX);
	i_g1i8(c, G1_ADD, RDX, '0');
	i_g1i8(c, G1_SUB, RDI, 1);
	i_movmr8(c, RDI, 0, RDX);
	i_g1i8(c, G1_ADD, RCX, 1);
	i_jmpto(c, looptarget);
	i_patchhere(c, jlex);
	i_patchhere(c, jafter0);
	i_testrr(c, R9, R9);
	jneg = i_jccph(c, CC_E);
	i_g1i8(c, G1_SUB, RDI, 1);
	i_movmi8(c, RDI, 0, '-');
	i_g1i8(c, G1_ADD, RCX, 1);
	i_patchhere(c, jneg);
	i_movmi8(c, RSP, 31, ' ');
	i_g1i8(c, G1_ADD, RCX, 1);
	i_movrr(c, RSI, RDI);
	i_movrr(c, RDX, RCX);
	i_movi32z(c, RDI, 1);
	i_movi32z(c, RAX, (unsigned long)sys_write);
	i_syscall(c);
	i_g1i8(c, G1_ADD, RSP, 32);
	i_ret(c);
}
 
static long
loopidxaddr(long depth)
{
	return looparea_addr + (depth * 16);
}
 
static long
looplimaddr(long depth)
{
	return looparea_addr + (depth * 16) + 8;
}
 
static void
do_if(buf* c)
{
	long p;
 
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_ADD, R15, 8);
	i_testrr(c, RAX, RAX);
	p = i_jccph(c, CC_E);
	cpush(CK_IF, p, 0);
}
 
static void
do_else(buf* c)
{
	cent e;
	long p;
 
	e = cpop();
	if(e.kind != CK_IF)
	{
		fail("else without matching if.");
	}
	p = i_jmpph(c);
	i_patchhere(c, e.a);
	cpush(CK_ELSE, p, 0);
}
 
static void
do_then(buf* c)
{
	cent e;
 
	e = cpop();
	if(e.kind != CK_IF && e.kind != CK_ELSE)
	{
		fail("then without matching if.");
	}
	i_patchhere(c, e.a);
}
 
static void
do_begin(buf* c)
{
	cpush(CK_BEGIN, here(c), 0);
}
 
static void
do_until(buf* c)
{
	cent e;
 
	e = cpop();
	if(e.kind != CK_BEGIN)
	{
		fail("until without matching begin.");
	}
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_ADD, R15, 8);
	i_testrr(c, RAX, RAX);
	i_jccto(c, CC_E, e.a);
}
 
static void
do_while(buf* c)
{
	cent e;
	long p;
 
	e = cpop();
	if(e.kind != CK_BEGIN)
	{
		fail("while without matching begin.");
	}
	i_movrm(c, RAX, R15, 0);
	i_g1i8(c, G1_ADD, R15, 8);
	i_testrr(c, RAX, RAX);
	p = i_jccph(c, CC_E);
	cpush(CK_WHILE, e.a, p);
}
 
static void
do_repeat(buf* c)
{
	cent e;
 
	e = cpop();
	if(e.kind != CK_WHILE)
	{
		fail("repeat without matching while.");
	}
	i_jmpto(c, e.a);
	i_patchhere(c, e.b);
}
 
static void
do_do(buf* c)
{
	long idxaddr;
	long limaddr;
 
	if(loopdepth == LOOP_MAX)
	{
		fail("do loops nested too deeply.");
	}
	idxaddr = loopidxaddr(loopdepth);
	limaddr = looplimaddr(loopdepth);
	i_movrm(c, RAX, R15, 0);
	i_movrm(c, RBX, R15, 8);
	i_g1i8(c, G1_ADD, R15, 16);
	i_movi32z(c, RCX, (unsigned long)idxaddr);
	i_movmr(c, RCX, 0, RAX);
	i_movi32z(c, RCX, (unsigned long)limaddr);
	i_movmr(c, RCX, 0, RBX);
	cpush(CK_DO, here(c), loopdepth);
	loopdepth = loopdepth + 1;
}
 
static void
do_i(buf* c)
{
	long k;
	long idxaddr;
	int found;
 
	found = 0;
	idxaddr = 0;
	for(k = ctop - 1; k >= 0; k = k - 1)
	{
		if(cstack[k].kind == CK_DO)
		{
			idxaddr = loopidxaddr(cstack[k].b);
			found = 1;
			break;
		}
	}
	if(found == 0)
	{
		fail("i used outside do loop.");
	}
	i_movi32z(c, RCX, (unsigned long)idxaddr);
	i_movrm(c, RAX, RCX, 0);
	i_g1i8(c, G1_SUB, R15, 8);
	i_movmr(c, R15, 0, RAX);
}
 
static void
do_loop(buf* c)
{
	cent e;
	long idxaddr;
	long limaddr;
 
	e = cpop();
	if(e.kind != CK_DO)
	{
		fail("loop without matching do.");
	}
	idxaddr = loopidxaddr(e.b);
	limaddr = looplimaddr(e.b);
	i_movi32z(c, RCX, (unsigned long)idxaddr);
	i_movrm(c, RAX, RCX, 0);
	i_g1i8(c, G1_ADD, RAX, 1);
	i_movi32z(c, RCX, (unsigned long)idxaddr);
	i_movmr(c, RCX, 0, RAX);
	i_movi32z(c, RCX, (unsigned long)limaddr);
	i_movrm(c, RBX, RCX, 0);
	i_alurr(c, 0x39, RBX, RAX);
	i_jccto(c, CC_L, e.a);
	loopdepth = loopdepth - 1;
}
 
static void
do_colon(buf* c, const char* name)
{
	long p;
	long bodyaddr;
 
	if(compiling != 0)
	{
		fail("nested colon definition.");
	}
	p = i_jmpph(c);
	bodyaddr = here(c);
	dictadd(name, DK_WORD, bodyaddr);
	cpush(CK_COLON, p, bodyaddr);
	compiling = 1;
	curwordaddr = bodyaddr;
}
 
static void
do_semi(buf* c)
{
	cent e;
 
	e = cpop();
	if(e.kind != CK_COLON)
	{
		fail("semicolon without matching colon.");
	}
	i_ret(c);
	i_patchhere(c, e.a);
	compiling = 0;
}
 
static void
do_recurse(buf* c)
{
	if(compiling == 0)
	{
		fail("recurse outside colon definition.");
	}
	i_callto(c, curwordaddr);
}
 
static void
do_variable(const char* name)
{
	long addr;
 
	addr = vars_addr + (varcount * 8);
	varcount = varcount + 1;
	dictadd(name, DK_VAR, addr);
}
 
static void
do_constant(const char* name, long val)
{
	dictadd(name, DK_CONST, val);
}
 
static long
opfind(const char* name)
{
	long i;
 
	i = 0;
	while(PRIMS[i].name != NULL)
	{
		if(strcmp(name, PRIMS[i].name) == 0)
		{
			return i;
		}
		i = i + 1;
	}
	return -1;
}
 
static void
genop(buf* c, long idx)
{
	switch(PRIMS[idx].op)
	{
		case OP_ADD:
			gen_add(c);
			break;
		case OP_SUB:
			gen_sub(c);
			break;
		case OP_MUL:
			gen_mul(c);
			break;
		case OP_DIV:
			gen_divmod(c, 0);
			break;
		case OP_MOD:
			gen_divmod(c, 1);
			break;
		case OP_DUP:
			gen_dup(c);
			break;
		case OP_DROP:
			gen_drop(c);
			break;
		case OP_SWAP:
			gen_swap(c);
			break;
		case OP_OVER:
			gen_over(c);
			break;
		case OP_ROT:
			gen_rot(c);
			break;
		case OP_EQ:
			gen_cmp(c, CC_NE);
			break;
		case OP_NE:
			gen_cmp(c, CC_E);
			break;
		case OP_LT:
			gen_cmp(c, CC_GE);
			break;
		case OP_GT:
			gen_cmp(c, CC_LE);
			break;
		case OP_LE:
			gen_cmp(c, CC_G);
			break;
		case OP_GE:
			gen_cmp(c, CC_L);
			break;
		case OP_ZEQ:
			gen_unarytest(c, CC_NE);
			break;
		case OP_ZLT:
			gen_unarytest(c, CC_NS);
			break;
		case OP_AND:
			gen_bitwise(c, 0x21);
			break;
		case OP_OR:
			gen_bitwise(c, 0x09);
			break;
		case OP_XOR:
			gen_bitwise(c, 0x31);
			break;
		case OP_INVERT:
			gen_unaryf7(c, F7_NOT);
			break;
		case OP_NEG:
			gen_unaryf7(c, F7_NEG);
			break;
		case OP_INC:
			gen_incdec(c, G1_ADD);
			break;
		case OP_DEC:
			gen_incdec(c, G1_SUB);
			break;
		case OP_FETCH:
			gen_fetch(c);
			break;
		case OP_STORE:
			gen_store(c);
			break;
		case OP_PLUSSTORE:
			gen_plusstore(c);
			break;
		case OP_EMIT:
			gen_emit(c);
			break;
		case OP_DOT:
			gen_dot(c);
			break;
		case OP_CR:
			gen_emitcharimm(c, 10);
			break;
		case OP_SPACE:
			gen_emitcharimm(c, ' ');
			break;
		case OP_TRUE:
			gen_lit(c, 1);
			break;
		case OP_FALSE:
			gen_lit(c, 0);
			break;
		case OP_BYE:
			gen_bye(c);
			break;
		case OP_NIP:
			gen_swap(c);
			gen_drop(c);
			break;
		default:
			fail("bad op.");
			break;
	}
}
 
static void
compile(buf* c)
{
	long pos;
	char* t;
	long litval;
	long oi;
	long di;
 
	pos = 0;
	while(pos < ntok)
	{
		t = toks[pos];
 
		if(tokisstr[pos] != 0)
		{
			gen_dotquote(c, t, (long)strlen(t));
			pos = pos + 1;
			continue;
		}
 
		if(isnum(t, &litval) != 0)
		{
			if(pos + 1 < ntok && strcmp(toks[pos + 1], "constant") == 0)
			{
				if(pos + 2 >= ntok)
				{
					fail("constant needs a name.");
				}
				do_constant(toks[pos + 2], litval);
				pos = pos + 3;
				continue;
			}
			gen_lit(c, litval);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, ":") == 0)
		{
			if(pos + 1 >= ntok)
			{
				fail("colon needs a name.");
			}
			do_colon(c, toks[pos + 1]);
			pos = pos + 2;
			continue;
		}
		if(strcmp(t, ";") == 0)
		{
			do_semi(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "if") == 0)
		{
			do_if(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "else") == 0)
		{
			do_else(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "then") == 0)
		{
			do_then(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "begin") == 0)
		{
			do_begin(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "until") == 0)
		{
			do_until(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "while") == 0)
		{
			do_while(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "repeat") == 0)
		{
			do_repeat(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "do") == 0)
		{
			do_do(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "loop") == 0)
		{
			do_loop(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "i") == 0)
		{
			do_i(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "recurse") == 0)
		{
			do_recurse(c);
			pos = pos + 1;
			continue;
		}
		if(strcmp(t, "variable") == 0)
		{
			if(pos + 1 >= ntok)
			{
				fail("variable needs a name.");
			}
			do_variable(toks[pos + 1]);
			pos = pos + 2;
			continue;
		}
 
		oi = opfind(t);
		if(oi >= 0)
		{
			genop(c, oi);
			pos = pos + 1;
			continue;
		}
 
		di = dictfind(t);
		if(di >= 0)
		{
			if(dict[di].kind == DK_WORD)
			{
				i_callto(c, dict[di].val);
			}
			else
			{
				gen_lit(c, dict[di].val);
			}
			pos = pos + 1;
			continue;
		}
 
		failat(toklines[pos], "undefined word", t);
	}
 
	if(compiling != 0)
	{
		fail("unterminated colon definition.");
	}
	if(ctop != 0)
	{
		fail("unterminated control structure.");
	}
}
 
static long
osphnum(long os)
{
	if(os == OS_NETBSD)
	{
		return 3;
	}
	return 2;
}
 
static long
osnotesize(long os)
{
	if(os == OS_NETBSD)
	{
		return NOTESIZE;
	}
	return 0;
}
 
static void
emitelf(buf* o, long entry, long totalsize, long os)
{
	long i;
	long phnum;
	long notesize;
	long noteoff;
 
	phnum = osphnum(os);
	notesize = osnotesize(os);
	noteoff = EHSIZE + (PHSIZE * phnum);
 
	bufbyte(o, 0x7F);
	bufbyte(o, 'E');
	bufbyte(o, 'L');
	bufbyte(o, 'F');
	bufbyte(o, 2);
	bufbyte(o, 1);
	bufbyte(o, 1);
	bufbyte(o, 0);
	for(i = 0; i < 8; i = i + 1)
	{
		bufbyte(o, 0);
	}
	bufu16(o, 2);
	bufu16(o, 0x3E);
	bufu32(o, 1);
	bufu64(o, entry);
	bufu64(o, EHSIZE);
	bufu64(o, 0);
	bufu32(o, 0);
	bufu16(o, EHSIZE);
	bufu16(o, PHSIZE);
	bufu16(o, phnum);
	bufu16(o, 0);
	bufu16(o, 0);
	bufu16(o, 0);
 
	if(notesize > 0)
	{
		bufu32(o, 4);
		bufu32(o, 4);
		bufu64(o, noteoff);
		bufu64(o, BASE + noteoff);
		bufu64(o, BASE + noteoff);
		bufu64(o, notesize);
		bufu64(o, notesize);
		bufu64(o, 4);
	}
 
	bufu32(o, 1);
	bufu32(o, 7);
	bufu64(o, 0);
	bufu64(o, BASE);
	bufu64(o, BASE);
	bufu64(o, totalsize);
	bufu64(o, totalsize);
	bufu64(o, 0x1000);
 
	/* data stack lives in its own segment, far from BASE and backed by no
	   file bytes (filesz 0, pure anonymous zero pages), non-executable.
	   over/underflowing it walks off the end of mapped memory and faults
	   right there instead of corrupting code or vars in the main segment. */
	bufu32(o, 1);
	bufu32(o, 6);
	bufu64(o, 0);
	bufu64(o, STACKBASE);
	bufu64(o, STACKBASE);
	bufu64(o, 0);
	bufu64(o, DSTACK_SIZE);
	bufu64(o, 0x1000);
}
 
static void
emitnote(buf* o)
{
	bufu32(o, 7);
	bufu32(o, 4);
	bufu32(o, 1);
	bufbyte(o, 'N');
	bufbyte(o, 'e');
	bufbyte(o, 't');
	bufbyte(o, 'B');
	bufbyte(o, 'S');
	bufbyte(o, 'D');
	bufbyte(o, 0);
	bufbyte(o, 0);
	bufu32(o, 1000000000);
}
 
int
main(int argc, char** argv)
{
	const char* infile;
	const char* outfile;
	const char* target;
	const char* pos[2];
	long npos;
	long ai;
	FILE* in;
	FILE* out;
	char* src;
	long srclen;
	long numvars;
	long i;
	long n;
	buf code;
	buf out2;
	long totalsize;
	long datasize;
	long phnum;
	long notesize;
	long hdrsz;
 
	target = NULL;
	npos = 0;
	ai = 1;
	while(ai < argc)
	{
		if(strcmp(argv[ai], "-b") == 0)
		{
			if(ai + 1 >= argc)
			{
				fprintf(stderr, "err: -b needs an argument.\n");
				return 1;
			}
			target = argv[ai + 1];
			ai = ai + 2;
			continue;
		}
		if(npos == 2)
		{
			fprintf(stderr, "err: bad usage.\n");
			fprintf(stderr, "usage: %s -b linux|netbsd [in.fs|-] [out]\n", argv[0]);
			return 1;
		}
		pos[npos] = argv[ai];
		npos = npos + 1;
		ai = ai + 1;
	}
 
	infile = "a.fs";
	outfile = "a.out";
	if(npos >= 1)
	{
		infile = pos[0];
	}
	if(npos >= 2)
	{
		outfile = pos[1];
	}
 
#ifdef FIXED_TARGET
	if(target != NULL && strcmp(target, FIXED_TARGET) != 0)
	{
		fprintf(stderr, "err: this build only supports -b %s.\n", FIXED_TARGET);
		return 1;
	}
	target = FIXED_TARGET;
#else
	if(target == NULL)
	{
		fprintf(stderr, "err: -b is required.\n");
		fprintf(stderr, "usage: %s -b linux|netbsd [in.fs|-] [out]\n", argv[0]);
		return 1;
	}
#endif
 
	if(strcmp(target, "linux") == 0)
	{
		targetos = OS_LINUX;
	}
	else if(strcmp(target, "netbsd") == 0)
	{
		targetos = OS_NETBSD;
	}
	else
	{
		fprintf(stderr, "err: unknown target '%s' (use linux or netbsd).\n", target);
		return 1;
	}
	if(targetos == OS_NETBSD)
	{
		sys_write = 4;
		sys_exit = 1;
	}
	else
	{
		sys_write = 1;
		sys_exit = 60;
	}
 
	if(strcmp(infile, "-") == 0)
	{
		in = stdin;
	}
	else
	{
		in = fopen(infile, "rb");
		if(in == NULL)
		{
			fprintf(stderr, "err: cant open %s.\n", infile);
			return 1;
		}
	}
 
	src = readall(in, &srclen);
	if(strcmp(infile, "-") != 0)
	{
		fclose(in);
	}
 
	tokenize(src, srclen);
	free(src);
 
	numvars = 0;
	for(i = 0; i < ntok; i = i + 1)
	{
		if(strcmp(toks[i], "variable") == 0)
		{
			numvars = numvars + 1;
		}
	}
 
	phnum = osphnum(targetos);
	notesize = osnotesize(targetos);
	hdrsz = EHSIZE + (PHSIZE * phnum);
 
	scratch_addr = BASE + hdrsz + notesize;
	looparea_addr = scratch_addr + SCRATCH_SIZE;
	vars_addr = looparea_addr + LOOPAREA_SIZE;
	codebase = vars_addr + (numvars * 8);
	datasize = codebase - scratch_addr;
 
	dstack_base = STACKBASE;
	dstack_top = dstack_base + DSTACK_SIZE;
 
	bufinit(&code);
	printnum_addr = here(&code);
	gen_printnum(&code);
	n = here(&code);
	i_movi32z(&code, R15, (unsigned long)dstack_top);
 
	compile(&code);
 
	gen_bye(&code);
 
	totalsize = (codebase - BASE) + code.len;
	if(BASE + totalsize > STACKBASE)
	{
		fail("program too large: grew past the reserved gap before the stack segment.");
	}
 
	bufinit(&out2);
	emitelf(&out2, n, totalsize, targetos);
	if(notesize > 0)
	{
		emitnote(&out2);
	}
	bufzero(&out2, datasize);
	bufappend(&out2, code.data, code.len);
 
	if(strcmp(outfile, "-") == 0)
	{
		fail("cant write elf to stdout.");
	}
	out = fopen(outfile, "wb");
	if(out == NULL)
	{
		fprintf(stderr, "err: cant create %s.\n", outfile);
		return 1;
	}
	fwrite(out2.data, 1, (size_t)out2.len, out);
	fclose(out);
	chmod(outfile, 0755);
 
	printf("ok: wrote %s with %ld bytes.\n", outfile, out2.len);
 
	return 0;
}
powered by btf.