~pixelherodev/c3lib

99c6613ff53403232de3ca2ca4ac031c29792acf — Noam Preil 5 months ago c32af26
Major refactor
15 files changed, 541 insertions(+), 566 deletions(-)

M Makefile
R tree.c => c3.c
A c3.h
M codegen/arm64.c
M codegen/c.c
M codegen/z80.c
M dump.c
M mkfile
M sema/expr.c
M sema/scope.c
M sema/scope.h
M sema/serialize.c
M sema/type.c
M sema/usage.c
D tree.h
M Makefile => Makefile +1 -1
@@ 6,7 6,7 @@ CFLAGS=-std=c17 -pedantic -Wall -O1 -march=native -g -D _POSIX_C_SOURCE=200809 -
all:libc3.a

borked: codegen/arm64.o codegen/z80.o
libc3.a: stb_ds.o util.o tree.o dump.o sema/scope.o sema/usage.o sema/serialize.o sema/type.o sema/expr.o tags.o codegen/c.o
libc3.a: stb_ds.o util.o c3.o dump.o sema/scope.o sema/usage.o sema/serialize.o sema/type.o sema/expr.o tags.o codegen/c.o
	rm -f libc3.a
	$(AR) $(ARFLAGS) $@ $^


R tree.c => c3.c +131 -82
@@ 7,39 7,59 @@
#include <string.h>

#include "tags.h"
#include "tree.h"
#include "c3.h"
#include "util.h"
#include "stb.h"

void
c3init(struct c3tree *tree)
c3init(c3ctx *ctx)
{
	// TODO: stop using this workaround
	struct c3tree result = {
		.version = 0,
		.nodes = NULL,
		.strings = NULL,
		.chars = NULL,
	c3ctx result = {
		.core = {
			.version = 0,
			.strings = NULL,
			.chars = NULL,
		},
		.ast = {
			.nodes = NULL,
		},
		.analysis = {
			.tests = NULL,
			.exports = NULL,
			.types = NULL,
			.deps = NULL,
			.callgraph = NULL,
			.names = NULL,
		},
		.debug = {
			.linenumbers = NULL,
			.columnnumbers = NULL,
			.linestrings = NULL,
		},
		.dedup = {
			.types = NULL,
		},
	};
	if(c3append(&result, C3_CONTEXTUAL, 1, 0) == -1)
		FATAL("OOM", 0);
	*tree = result;
	*ctx = result;
}

void
c3free(struct c3tree *tree)
c3free(c3ctx *ctx)
{
	stb_sb_free(tree->nodes);
	stb_sb_free(tree->strings);
	stb_sb_free(tree->chars);
	free(tree);
	stb_sb_free(ctx->ast.nodes);
	stb_sb_free(ctx->core.strings);
	stb_sb_free(ctx->core.chars);
	free(ctx);
}

#define WFAIL() {ERROR("Failed to write tree", 0); return 0;}
#define RFAIL() {ERROR("Failed to read tree",0);return 0;}
#define WFAIL() {ERROR("Failed to write ctx", 0); return 0;}
#define RFAIL() {ERROR("Failed to read ctx",0);return 0;}

static int
c3tree_read_array(void *_array, uint32_t elemsize, int fd)
c3ctx_read_array(void *_array, uint32_t elemsize, int fd)
{
	uint32_t count;
	void **array = _array;


@@ 54,7 74,7 @@ c3tree_read_array(void *_array, uint32_t elemsize, int fd)
}

static int
c3tree_write_array(void *_array, uint32_t elemsize, int fd)
c3ctx_write_array(void *_array, uint32_t elemsize, int fd)
{
	void **array = _array;
	uint32_t count = stb_sb_count(*array);


@@ 65,161 85,190 @@ c3tree_write_array(void *_array, uint32_t elemsize, int fd)
	return 1;
}

static int
c3ctxwritemap(c3nodemap *map, int fd)
{
	return 0;
}

int
c3read(struct c3tree *tree, int fd)
c3read(c3ctx *ctx, int fd)
{
	if(read(fd, &tree->version, 4) != 4)
	c3init(ctx);
	if(read(fd, &ctx->core.version, 4) != 4)
		RFAIL();
	tree->nodes = NULL;
	tree->strings = NULL;
	tree->chars = NULL;
	if(!c3tree_read_array(&tree->nodes, 4, fd))
	if(!c3ctx_read_array(&ctx->ast.nodes, 4, fd))
		RFAIL();
	if(!c3tree_read_array(&tree->strings, sizeof(struct c3string), fd))
	if(!c3ctx_read_array(&ctx->core.strings, sizeof(c3string), fd))
		RFAIL();
	if(!c3tree_read_array(&tree->chars, 1, fd))
	if(!c3ctx_read_array(&ctx->core.chars, 1, fd))
		RFAIL();
	return 1;
}

int
c3write(struct c3tree *tree, int fd)
static int
c3corewrite(c3core core, int fd)
{
	if(write(fd, &core.version, 4) != 4)
		WFAIL();
	if(!c3ctx_write_array(&core.strings, sizeof(c3string), fd))
		WFAIL();
	if(!c3ctx_write_array(&core.chars, 1, fd))
		WFAIL();
	return 1;
}

static int
c3astwrite(c3ast ast, int fd)
{
	if(write(fd, &tree->version, 4) != 4)
	if(!c3ctx_write_array(&ast.nodes, 4, fd))
		WFAIL();
	if(!c3tree_write_array(&tree->nodes, 4, fd))
	return 1;
}

static int
c3analysiswrite(c3analysis analysis, int fd)
{
	if(!c3ctxwritemap(analysis.deps, fd))
		WFAIL();
	if(!c3tree_write_array(&tree->strings, sizeof(struct c3string), fd))
	return 1;
}

int
c3write(c3ctx *ctx, int fd)
{
	if(!c3corewrite(ctx->core, fd))
		WFAIL();
	if(!c3astwrite(ctx->ast, fd))
		WFAIL();
	if(!c3tree_write_array(&tree->chars, 1, fd))
	if(!c3analysiswrite(ctx->analysis, fd))
		WFAIL();
	return 1;
}

uint32_t
c3append(struct c3tree *tree, c3tag tag, uint16_t kid_count, ...)
c3append(c3ctx *ctx, c3tag tag, uint16_t kid_count, ...)
{
	va_list args;
	uint32_t i;
	uint32_t index;
	if(!stb_sb_ensure_capacity(&tree->nodes, 1 + kid_count, 4))
	if(!stb_sb_ensure_capacity(&ctx->ast.nodes, 1 + kid_count, 4))
		return -1;
	index = stb_sb_count(tree->nodes);
	c3nodeset(*tree, index, tag, kid_count);
	index = stb_sb_count(ctx->ast.nodes);
	c3nodeset(*ctx, index, tag, kid_count);
	// A single node actually takes up 8 bytes, or two uint32 entries
	va_start(args, kid_count);
	for(i = 0; i < kid_count; i += 1)
		tree->nodes[index + 1 + i] = va_arg(args, uint32_t);
	stb_sb_increment(tree->nodes, kid_count + 1);
		ctx->ast.nodes[index + 1 + i].node_index = va_arg(args, uint32_t);
	stb_sb_increment(ctx->ast.nodes, kid_count + 1);
	va_end(args);
	return index;
}

uint32_t
c3appendarr(struct c3tree *tree, c3tag tag, uint16_t kid_count, uint32_t *kids)
c3appendarr(c3ctx *ctx, c3tag tag, uint16_t kid_count, uint32_t *kids)
{
	uint32_t i;
	uint32_t index;
	if(!stb_sb_ensure_capacity(&tree->nodes, 1 + kid_count, 4))
	if(!stb_sb_ensure_capacity(&ctx->ast.nodes, 1 + kid_count, 4))
		return -1;
	index = stb_sb_count(tree->nodes);
	c3nodeset(*tree, index, tag, kid_count);
	index = stb_sb_count(ctx->ast.nodes);
	c3nodeset(*ctx, index, tag, kid_count);
	// A single node actually takes up 8 bytes, or two uint32 entries
	for(i = 0; i < kid_count; i += 1)
		tree->nodes[index + 1 + i] = kids[i];
	stb_sb_increment(tree->nodes, kid_count + 1);
		ctx->ast.nodes[index + 1 + i].node_index = kids[i];
	stb_sb_increment(ctx->ast.nodes, kid_count + 1);
	return index;
}

uint32_t
c3append_chars(struct c3tree *tree, char *ptr, uint32_t len)
c3append_chars(c3ctx *ctx, char *ptr, uint32_t len)
{
	uint32_t index;
	if(!stb_sb_ensure_capacity(&tree->chars, len, 1))
	if(!stb_sb_ensure_capacity(&ctx->core.chars, len, 1))
		return -1;
	index = stb_sb_count(tree->chars);
	strncpy(tree->chars + index, ptr, len);
	stb_sb_increment(tree->chars, len);
	index = stb_sb_count(ctx->core.chars);
	strncpy(ctx->core.chars + index, ptr, len);
	stb_sb_increment(ctx->core.chars, len);
	return index;
}

uint32_t
c3append_string(struct c3tree *tree, char *ptr, uint32_t len)
c3append_string(c3ctx *ctx, char *ptr, uint32_t len)
{
	uint32_t index, cindex;
	if(!stb_sb_ensure_capacity(&tree->strings, 1, sizeof(struct c3string)))
	if(!stb_sb_ensure_capacity(&ctx->core.strings, 1, sizeof(c3string)))
		return -1;
	cindex = c3append_chars(tree, ptr, len);
	cindex = c3append_chars(ctx, ptr, len);
	if(cindex == -1)
		return -1;
	index = stb_sb_count(tree->strings);
	tree->strings[index] = (struct c3string){cindex, len};
	stb_sb_increment(tree->strings, 1);
	index = stb_sb_count(ctx->core.strings);
	ctx->core.strings[index] = (c3string){cindex, len};
	stb_sb_increment(ctx->core.strings, 1);
	return index;
}

char *
h_c3str(struct c3tree tree, uint32_t string_handle)
h_c3str(c3ctx ctx, uint32_t string_handle)
{
	char *buf;
	struct c3string string;
	string = tree.strings[string_handle];
	c3string string;
	string = ctx.core.strings[string_handle];
	buf = malloc(string.length + 1);
	if(buf == NULL)
		return NULL;
	strncpy(buf, &tree.chars[string.index], string.length);
	strncpy(buf, &ctx.core.chars[string.index], string.length);
	buf[string.length] = 0;
	return buf;
}

uint32_t
c3nodechild(struct c3tree tree, uint32_t node, uint32_t child)
c3nodechild(c3ctx ctx, uint32_t node, uint32_t child)
{
	uint32_t index;
	index = node + 1 + child;
	if(index >= stb_sb_count(tree.nodes) || tree.nodes[index] >= stb_sb_count(tree.nodes))
	if(index >= stb_sb_count(ctx.ast.nodes) || ctx.ast.nodes[index].node_index >= stb_sb_count(ctx.ast.nodes))
		return -1;
	return tree.nodes[index];
	return ctx.ast.nodes[index].node_index;
}

c3tag
c3nodetag(struct c3tree tree, uint32_t node)
c3nodetag(c3ctx ctx, uint32_t node)
{
	void *raw = &tree.nodes[node];
	uint16_t *val = raw;
	return *val;
	return ctx.ast.nodes[node].tag;
}

uint16_t
c3nodekids(struct c3tree tree, uint32_t node)
c3nodekids(c3ctx ctx, uint32_t node)
{
	void *raw = &tree.nodes[node];
	uint16_t *val = raw;
	return val[1];
	return ctx.ast.nodes[node].kids;
}

void
c3nodeset(struct c3tree tree, uint32_t index, c3tag tag, uint16_t kid_count)
c3nodeset(c3ctx ctx, uint32_t index, c3tag tag, uint16_t kid_count)
{
	void *ptr;
	uint16_t *val;
	if(index > stb_sb_count(tree.nodes))
	void *raw;
	c3node *ptr;
	c3node node = {
		.tag = tag,
		.kids = kid_count
	};
	if(index > stb_sb_count(ctx.ast.nodes))
		FATAL("ICE: index out of bounds: %d", index);
	ptr = &tree.nodes[index];
	val = ptr;
	*val = tag;
	*(val + 1) = kid_count;
	raw = &ctx.ast.nodes[index];
	ptr = raw;
	*ptr = node;
}

uint32_t
c3intern(struct c3tree *tree, char *ptr, uint32_t len)
c3intern(c3ctx *ctx, char *ptr, uint32_t len)
{
	uint32_t handle;
	uint32_t count;
	count = stb_sb_count(tree->strings);
	count = stb_sb_count(ctx->core.strings);
	for(handle = 1; handle < count; handle += 1)
		if(len == tree->strings[handle].length)
			if(strncmp(tree->chars + tree->strings[handle].index, ptr, len) == 0)
		if(len == ctx->core.strings[handle].length)
			if(strncmp(ctx->core.chars + ctx->core.strings[handle].index, ptr, len) == 0)
				return handle;
	return c3append_string(tree, ptr, len);
	return c3append_string(ctx, ptr, len);
}

A c3.h => c3.h +95 -0
@@ 0,0 1,95 @@
typedef struct{
	uint32_t index;
	uint32_t length;
} c3string;

typedef struct{
	uint32_t version;
	c3string *strings;
	char *chars;
} c3core;

typedef union{
	uint32_t node_index;
	uint32_t string_index;
	struct{
		uint16_t tag;
		uint16_t kids;
	};
} c3node;

typedef struct{
	c3node *nodes;
} c3ast;

typedef struct{
	c3node *nodes;
} c3ir;

// Maps a node to some arbitrary u32, whose meaning depends on the map.
// Primarily useful for mapping nodes to other nodes
typedef struct{
	uint32_t key;
	uint32_t value;
} c3nodemap;

typedef struct{
	uint32_t *tests;
	uint32_t *exports;
	uint32_t *fns;
	// Maps functions to the functions they call. Each target node is a C3_DEP node whose children form the list of callees
	// Bottom nodes (functions which call no other functions) map to -1. Since all functions are present in the graph,
	// we can iterate over the keys for a full list of functions, which is useful for e.g. codegen.
	c3nodemap *callgraph;
	c3nodemap *types;
	c3nodemap *deps;
	c3nodemap *names;
} c3analysis;

typedef struct {
	c3nodemap *linenumbers;
	c3nodemap *columnnumbers;
	c3nodemap *linestrings;
} c3debug;

// Used to track pieces of the tree for deduplication
typedef struct {
	uint32_t *types;
} c3dedup;

typedef struct{
	c3core core;
	union{
		c3ast ast;
		c3ir ir;
	};
	c3dedup dedup;
	c3analysis analysis;
	// Optional. Might be all NULL if debug info isn't requested
	c3debug debug;
} c3ctx;

// All functions which take in an index require bounds checking to be handled by
// the caller.
void c3init(c3ctx *);
void c3free(c3ctx *);
int c3read(c3ctx*, int fd);
int c3write(c3ctx*, int fd);

// If there is not sufficient space, this attempts to reserve space at a rate expanding exponentially
// This returns the new index, or -1 on failure
// Node-related functions (e.g. append, nodedump) interact with whichever node tree is active (AST or IR)
// String-related functions (e.g. interning) interact with the core
uint32_t c3append(c3ctx *, c3tag tag, uint16_t kid_count, ...);
uint32_t c3appendarr(c3ctx *, c3tag tag, uint16_t kid_count, uint32_t *kids);
uint32_t c3intern(c3ctx *, char *, uint32_t len);
char *h_c3str(c3ctx, uint32_t string_handle);

void c3nodedump(c3ctx tree, uint32_t index, uint32_t indent, int recurse);
void c3dump(c3ctx tree);

c3tag c3nodetag(c3ctx, uint32_t index);
uint16_t c3nodekids(c3ctx, uint32_t index);
uint32_t c3nodechild(c3ctx, uint32_t index, uint32_t child);
void c3nodeset(c3ctx, uint32_t index, c3tag tag, uint16_t kid_count);


M codegen/arm64.c => codegen/arm64.c +1 -1
@@ 4,7 4,7 @@
#include <string.h>

#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../util.h"
#include "../stb.h"


M codegen/c.c => codegen/c.c +62 -81
@@ 4,9 4,10 @@
#include <string.h>

#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../util.h"
#include "../stb.h"
#include "../stb_ds.h"

static struct {
	FILE *out;


@@ 21,14 22,14 @@ static struct {
	uint32_t nested;
	uint32_t counter;
	uint32_t indent;
	struct c3tree tree;
	c3ctx c3;
	struct{
		uint32_t *node;
		uint32_t *tmp;
	} regalloc;
} ctx;

static int cg_block(struct c3tree tree, uint32_t index);
static int cg_block(uint32_t index);

static uint32_t
cg_find_reg(uint32_t node)


@@ 50,13 51,7 @@ static int
cg_params(uint32_t index)
{
	uint32_t count;
	uint32_t i;
	uint32_t kid;
	uint32_t reg;
	char *buf;
	//regs 8-15 are used
	reg = 8;
	count = c3nodekids(ctx.tree, index);
	count = c3nodekids(ctx.c3, index);
	if(count != 1){
		ERROR("TODO: call args", 0);
		return 0;


@@ 67,7 62,7 @@ cg_params(uint32_t index)
static int
cg_value(uint32_t index)
{
	c3tag tag = c3nodetag(ctx.tree, index);
	c3tag tag = c3nodetag(ctx.c3, index);
	uint32_t tmp;
	switch(tag){
	case C3_VALUE_TRUE:


@@ 94,7 89,7 @@ static int
cg_type(uint32_t index)
{
	c3tag tag;
	tag = c3nodetag(ctx.tree, index);
	tag = c3nodetag(ctx.c3, index);
	switch(tag){
	case C3_TYPE_BOOL:
		fprintf(ctx.out, "bool");


@@ 124,12 119,12 @@ cg_call(uint32_t index)
	for(i = 0; i < ctx.indent; i += 1)
		fprintf(ctx.out, "\t");
	// IR FN
	node = c3nodechild(ctx.tree, index, 0);
	node = c3nodechild(ctx.c3, index, 0);
	// VALUE_FN
	node = c3nodechild(ctx.tree, index, 0);
	fntype = c3nodechild(ctx.tree, node, 0);
	rettype = c3nodechild(ctx.tree, fntype, 0);
	if(c3nodetag(ctx.tree, rettype) != C3_VOID){
	node = c3nodechild(ctx.c3, index, 0);
	fntype = c3nodechild(ctx.c3, node, 0);
	rettype = c3nodechild(ctx.c3, fntype, 0);
	if(c3nodetag(ctx.c3, rettype) != C3_VOID){
		if(!cg_type(rettype)){
			ERROR("type gen failed", 0);
			return 0;


@@ 139,7 134,7 @@ cg_call(uint32_t index)
			return 0;
		ctx.counter += 1;
	}
	buf = h_c3str(ctx.tree, c3nodechild(ctx.tree, c3nodechild(ctx.tree, node, 2), 0));
	buf = h_c3str(ctx.c3, c3nodechild(ctx.c3, c3nodechild(ctx.c3, node, 2), 0));
	fprintf(ctx.out, "%s();\n", buf);
	free(buf);
	return 1;


@@ 154,9 149,9 @@ cg_asm(uint32_t index)
	c3tag tag;
	uint32_t tmp;
	char *new;
	string = c3nodechild(ctx.tree, index, 0);
	string = c3nodechild(ctx.tree, string, 0);
	buf = h_c3str(ctx.tree, string);
	string = c3nodechild(ctx.c3, index, 0);
	string = c3nodechild(ctx.c3, string, 0);
	buf = h_c3str(ctx.c3, string);
	new = NULL;
	for(i = 0; i < strlen(buf); i += 1){
		if(buf[i] == '$'){


@@ 169,8 164,8 @@ cg_asm(uint32_t index)
				break;
			}
			if(buf[i] >= '0' && buf[i] <= '9'){
				tmp = c3nodechild(ctx.tree, index, buf[i] - '0');
				tag = c3nodetag(ctx.tree, tmp);
				tmp = c3nodechild(ctx.c3, index, buf[i] - '0');
				tag = c3nodetag(ctx.c3, tmp);
				switch(tag){
				default:
					ERROR("TODO: asm substitute local register param %c tag %s", buf[i], c3tagstr(tag));


@@ 180,11 175,11 @@ cg_asm(uint32_t index)
			else if(buf[i] == '*'){
				i += 1;
				if(i < strlen(buf) && buf[i] >= '0' && buf[i] <= '9'){
					tmp = c3nodechild(ctx.tree, index, buf[i] - '0');
					tag = c3nodetag(ctx.tree, tmp);
					tmp = c3nodechild(ctx.c3, index, buf[i] - '0');
					tag = c3nodetag(ctx.c3, tmp);
					switch(tag){
					case C3_LITERAL_STRING:
						buf2 = h_c3str(ctx.tree, c3nodechild(ctx.tree, tmp, 0));
						buf2 = h_c3str(ctx.c3, c3nodechild(ctx.c3, tmp, 0));
						if(!stb_stringbuf_append(&new, buf2)){
							ERROR("ICE: OOM", 0);
							return 0;


@@ 221,11 216,11 @@ cg_asm(uint32_t index)
static int
cg_cond_br(uint32_t index)
{
	uint32_t from = c3nodechild(ctx.tree, index, 0);
	uint32_t from = c3nodechild(ctx.c3, index, 0);
	uint32_t i;
	if(c3nodetag(ctx.tree, from) == C3_IR_INVERT){
		from = c3nodechild(ctx.tree, from, 0);
		if(c3nodetag(ctx.tree, from) == C3_VALUE_TRUE)
	if(c3nodetag(ctx.c3, from) == C3_IR_INVERT){
		from = c3nodechild(ctx.c3, from, 0);
		if(c3nodetag(ctx.c3, from) == C3_VALUE_TRUE)
			return 1;
		for(i = 0; i < ctx.indent; i += 1)
			fprintf(ctx.out, "\t");


@@ 246,7 241,7 @@ cg_cond_br(uint32_t index)
static int
cg_invert(uint32_t index)
{
	uint32_t from = c3nodechild(ctx.tree, index, 0);
	uint32_t from = c3nodechild(ctx.c3, index, 0);
	uint32_t count = ctx.counter;
	uint32_t i;
	ctx.counter += 1;


@@ 262,19 257,11 @@ cg_invert(uint32_t index)
static int
cg_set(uint32_t index)
{
	char *buf;
	ERROR("TODO: codegen IR_SET", 0);
	return 0;
}		

static int
cg_binop(uint32_t index, const char *const op)
{
	ERROR("TODO: codegen binop", 0);
	return 0;
}

static int
cg_return(uint32_t index)
{
	uint32_t i;


@@ 282,8 269,8 @@ cg_return(uint32_t index)
	for(i = 0; i < ctx.indent; i += 1)
		fprintf(ctx.out, "\t");
	fprintf(ctx.out, "return");
	val = c3nodechild(ctx.tree, index, 1);
	if(c3nodetag(ctx.tree, val) != C3_VOID){
	val = c3nodechild(ctx.c3, index, 1);
	if(c3nodetag(ctx.c3, val) != C3_VOID){
		fprintf(ctx.out, " ");
		if(!cg_value(val)){
			ERROR("Failed to gen return value", 0);


@@ 298,15 285,13 @@ static int
cg_break(uint32_t index)
{
	uint32_t block;
	uint32_t expr;
	uint32_t sreg, treg;
	// id
	block = c3nodechild(ctx.tree, index, 0);
	block = c3nodechild(ctx.c3, index, 0);
	if(block >= stb_sb_count(ctx.blocks)){
		ERROR("ICE: invalid break block", 0);
		return 0;
	}
	if(c3nodekids(ctx.tree, index) == 2){
	if(c3nodekids(ctx.c3, index) == 2){
		ERROR("TODO: break values", 0);
		return 0;
	}


@@ 325,9 310,9 @@ static int
cg_panic(uint32_t index)
{
	uint32_t i;
	uint32_t arg = c3nodechild(ctx.tree, index, 0);
	uint32_t str = c3nodechild(ctx.tree, arg, 0);
	char *buf = h_c3str(ctx.tree, str);
	uint32_t arg = c3nodechild(ctx.c3, index, 0);
	uint32_t str = c3nodechild(ctx.c3, arg, 0);
	char *buf = h_c3str(ctx.c3, str);
	for(i = 0; i < ctx.indent; i += 1)
		fprintf(ctx.out, "\t");
	fprintf(ctx.out, "fprintf(stderr, \"%%s:%%d: %s\", __FILE__, __LINE__);\n", buf);


@@ 348,18 333,18 @@ cg_container(uint32_t index)
}

static int
cg_inst(struct c3tree tree, uint32_t index)
cg_inst(uint32_t index)
{
	uint32_t i;
	c3tag tag = c3nodetag(tree, index);
	c3tag tag = c3nodetag(ctx.c3, index);
	switch(tag){
	case C3_IR_RELOOP:
		for(i = 0; i < ctx.indent; i += 1)
			fprintf(ctx.out, "\t");
		fprintf(ctx.out, "continue;\n", ctx.current.block);
		fprintf(ctx.out, "continue;\n");
		return 1;
	case C3_BLOCK:
		return cg_block(tree, index);
		return cg_block(index);
	case C3_IR_CALL:
		return cg_call(index);
	case C3_IR_SET:


@@ 387,17 372,17 @@ cg_inst(struct c3tree tree, uint32_t index)
}

static int
cg_block(struct c3tree tree, uint32_t index)
cg_block(uint32_t index)
{
	uint32_t i;
	uint32_t block;
	uint32_t prev_block;
	uint32_t prev_nested = ctx.nested;
	uint32_t last;
	uint16_t kids = c3nodekids(tree, index);
	uint16_t kids = c3nodekids(ctx.c3, index);
	if(kids == 0)
		return 1;
	last = c3nodechild(tree, index, kids - 1);
	last = c3nodechild(ctx.c3, index, kids - 1);
	prev_block = ctx.current.block;
	block = ctx.next_block;
	ctx.next_block += 1;


@@ 409,14 394,14 @@ cg_block(struct c3tree tree, uint32_t index)
	}
	for(i = 0; i < ctx.indent; i += 1)
		fprintf(ctx.out, "\t");
	if(c3nodetag(tree, last) == C3_IR_RELOOP){
	if(c3nodetag(ctx.c3, last) == C3_IR_RELOOP){
		ctx.current.is_loop = 1;
		fprintf(ctx.out, "while(true)");
	}
	fprintf(ctx.out, "{\n", block);
	fprintf(ctx.out, "{\n/* block %d */\n", block);
	ctx.indent += 1;
	for(i = 0; i < c3nodekids(tree, index); i += 1)
		if(!cg_inst(tree, c3nodechild(tree, index, i)))
	for(i = 0; i < c3nodekids(ctx.c3, index); i += 1)
		if(!cg_inst(c3nodechild(ctx.c3, index, i)))
			return 0;
	ctx.current.block = prev_block;
	ctx.indent -= 1;


@@ 429,18 414,18 @@ cg_block(struct c3tree tree, uint32_t index)
}

static int
cg_fn(struct c3tree tree, uint32_t index)
cg_fn(uint32_t index)
{
	uint32_t i;
	uint32_t fntype = c3nodechild(tree, c3nodechild(tree, index, 1), 0);
	uint32_t rettype = c3nodechild(tree, fntype, 0);
	uint32_t params = c3nodechild(tree, fntype, 1);
	uint16_t param_count = c3nodekids(tree, params);
	uint32_t fntype = c3nodechild(ctx.c3, c3nodechild(ctx.c3, index, 1), 0);
	uint32_t rettype = c3nodechild(ctx.c3, fntype, 0);
	uint32_t params = c3nodechild(ctx.c3, fntype, 1);
	uint16_t param_count = c3nodekids(ctx.c3, params);
	ctx.blocks = NULL;
	ctx.counter = 0;
	ctx.regalloc.node = NULL;
	ctx.regalloc.tmp = NULL;
	ctx.current.name = h_c3str(tree, c3nodechild(tree, c3nodechild(tree, c3nodechild(tree, index, 1), 2), 0));
	ctx.current.name = h_c3str(ctx.c3, c3nodechild(ctx.c3, c3nodechild(ctx.c3, c3nodechild(ctx.c3, index, 1), 2), 0));
	if(!cg_type(rettype))
		return 0;
	fprintf(ctx.out, "\n%s(", ctx.current.name);


@@ 455,42 440,38 @@ cg_fn(struct c3tree tree, uint32_t index)
	ctx.indent = 0;
	ctx.current.block = 0;
	ctx.next_block = 0;
	for(i = 0; i < c3nodekids(tree, index) - 1; i += 1)
		if(!cg_block(tree, c3nodechild(tree, index, i)))
	for(i = 0; i < c3nodekids(ctx.c3, index) - 1; i += 1)
		if(!cg_block(c3nodechild(ctx.c3, index, i)))
			return 0;
	fprintf(ctx.out, "\n");
	return 1;
}

int
codegen_c(struct c3tree tree, FILE *file)
codegen_c(c3ctx c3, FILE *file)
{
	uint32_t i, base;
	uint32_t i;
	uint32_t index;
	uint32_t dep_node;
	uint16_t nodes;
	c3tag tag;
	base = tree.graphs.call;
	nodes = stb_sb_count(c3.analysis.fns);
	ctx.c3 = c3;
	ctx.out = file;
	fprintf(ctx.out, "#include <stdio.h>\n#include <stdbool.h>\n#define zyg_noreturn void\n\n");
	ctx.tree = tree;
	nodes = c3nodekids(tree, base);
	for(i = 0; i < nodes - 1; i += 1){
		index = c3nodechild(tree, base, i);
		index = c3nodechild(tree, index, 0);
		tag = c3nodetag(tree, index);
	for(i = 0; i < nodes; i += 1){
		index = c3.analysis.fns[i];
		index = c3nodechild(ctx.c3, index, 0);
		tag = c3nodetag(ctx.c3, index);
		switch(tag){
		case C3_VALUE_FN:
			ERROR("ICE: received function value in codegen (node %d of %d) - should have been lowered to IR by semantic extraction!", i + 1, nodes);
			return 0;
		case C3_IR_FN:
			if(!cg_fn(tree, index))
			if(!cg_fn(index))
				return 0;
			break;
		case C3_BAD:
			ERROR("ICE: unable to read child node %d of root node with child count %d", i - (base + 2) + 1,nodes);
			c3nodedump(tree, tree.nodes[0], 0, 0);
			fprintf(ctx.out, "\n");
			ERROR("ICE: unable to read child node %d of call graph", i);
			return 0;
		default:
			ERROR("TODO: codegen root node type %s (node %d of %d)", c3tagstr(tag), i + 1, nodes);

M codegen/z80.c => codegen/z80.c +1 -1
@@ 4,7 4,7 @@
#include <string.h>

#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../util.h"
#include "../stb.h"


M dump.c => dump.c +16 -16
@@ 3,17 3,17 @@
#include <stdint.h>
#include <string.h>
#include "tags.h"
#include "tree.h"
#include "c3.h"
#include "util.h"
#include "stb.h"

int just_tags = 1;

void
c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
c3nodedump(c3ctx ctx, uint32_t index, uint32_t indent, int recurse)
{
	c3tag tag = c3nodetag(tree, index);
	uint16_t kid_count = c3nodekids(tree, index);
	c3tag tag = c3nodetag(ctx, index);
	uint16_t kid_count = c3nodekids(ctx, index);
	int i;
	int j;
	char *buf;


@@ 26,8 26,8 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
	switch(tag){
	case C3_DEP:
		if(recurse){
			if(c3nodechild(tree, index, 0) != -1)
				c3nodedump(tree, c3nodechild(tree, index, 0), indent, 0);
			if(c3nodechild(ctx, index, 0) != -1)
				c3nodedump(ctx, c3nodechild(ctx, index, 0), indent, 0);
		}
		else
			printf("%u, ", index);


@@ 36,11 36,11 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
		if(kid_count > 1)
			printf(", depends on: ");
		for(i = 1; i < kid_count; i += 1)
			c3nodedump(tree, c3nodechild(tree, index, i), indent, 0);
			c3nodedump(ctx, c3nodechild(ctx, index, i), indent, 0);
		break;
	case C3_LITERAL_STRING:
	case C3_IDENT:
		buf = h_c3str(tree, c3nodechild(tree, index, 0));
		buf = h_c3str(ctx, c3nodechild(ctx, index, 0));
		printf("\"%s\"", buf);
		free(buf);
		break;


@@ 57,7 57,7 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
		printf("{");
		if(tag == C3_CONST || tag == C3_VAR){
			for(i = 0; i < kid_count; i += 1){
    				if(c3nodechild(tree, index, i) == -1)
    				if(c3nodechild(ctx, index, i) == -1)
        				break;
    				if(kid_count > 1){
        				if(i >= 1)


@@ 67,7 67,7 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
        					printf("\t");
    				}
				printf("{%d}%s", index+1+i, recurse ? "=" : "");
				c3nodedump(tree, c3nodechild(tree, index, i), indent + 1, recurse);
				c3nodedump(ctx, c3nodechild(ctx, index, i), indent + 1, recurse);
			}
			printf("\n");
			for(j = 0; j < indent; j += 1)


@@ 75,14 75,14 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
			printf("}");
		}
		else if(tag == C3_EXPORT || tag == C3_PUB){
			c3nodedump(tree, c3nodechild(tree, index, 0), indent, recurse);
			c3nodedump(ctx, c3nodechild(ctx, index, 0), indent, recurse);
			printf("}");
		}
		else{
			for(i = 0; i < kid_count; i += 1){
				if(tag == C3_EXPR_RETURN && i == 0)
					continue;
    				if(c3nodechild(tree, index, i) == -1)
    				if(c3nodechild(ctx, index, i) == -1)
        				break;
    				if(kid_count > 1){
        				if(i >= 1)


@@ 91,7 91,7 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
        				for(j = 0; j < indent + 1; j += 1)
        					printf("\t");
    				}
				c3nodedump(tree, c3nodechild(tree, index, i), indent + (kid_count > 1 ? 1 : 0), recurse);
				c3nodedump(ctx, c3nodechild(ctx, index, i), indent + (kid_count > 1 ? 1 : 0), recurse);
			}
			if(kid_count > 1){
        			printf("\n");


@@ 105,11 105,11 @@ c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse)
}

void
c3dump(struct c3tree tree)
c3dump(c3ctx ctx)
{
	if(stb_sb_count(tree.nodes) == 0)
	if(stb_sb_count(ctx.ast.nodes) == 0)
    		FATAL("missing root", 0);
	c3nodedump(tree, tree.nodes[1], 0, 1);
	c3nodedump(ctx, ctx.ast.nodes[1].node_index, 0, 1);
	printf("\n");
}


M mkfile => mkfile +1 -1
@@ 4,7 4,7 @@ all:V: $O.out.libc3

borked: codegen/z80.$O codegen/arm64.$O codegen/c.$O

$O.out.libc3: util.$O stb_ds.$O tree.$O tags.$O dump.$O sema/usage.$O sema/scope.$O sema/serialize.$O sema/type.$O sema/expr.$O
$O.out.libc3: util.$O stb_ds.$O c3.$O tags.$O dump.$O sema/usage.$O sema/scope.$O sema/serialize.$O sema/type.$O sema/expr.$O
	ar ruv $target $prereq

install:V: /$objtype/lib/ape/libc3.a

M sema/expr.c => sema/expr.c +128 -135
@@ 5,9 5,10 @@
#include <string.h>

#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../util.h"
#include "../stb.h"
#include "../stb_ds.h"

// Lowerizer operates, effectively, on the function level
// The lowerizer takes in functions in base form - using expressions and stems - and replaces it with IR, linking to the original for future analysis


@@ 29,7 30,7 @@ static struct{
		uint32_t *values;
		uint32_t *irs;
	} fn_map;
	struct c3tree *tree;
	c3ctx *c3;
	uint32_t next_block_id;
	// during normal analysis, this is set to zero. When inlining a function, this is set to 1. l_block, when inline, sets this to the return value.
	// TODO: add IR for breaking with a value, and use that instead of this hack.


@@ 47,7 48,7 @@ s_map(uint32_t index)
	return -1;
}

static uint32_t l_block(struct c3tree *tree, uint32_t index, int type);
static uint32_t l_block(uint32_t index, int type);
static uint32_t s_expr(uint32_t index, uint32_t **list);
static uint32_t s_decl(uint32_t index);
uint32_t t_dedup_tag(uint32_t tag);


@@ 55,7 56,7 @@ uint32_t t_dedup_tag(uint32_t tag);
static uint32_t
s_defer(uint32_t index)
{
	if(s_expr(c3nodechild(*ctx.tree, index, 0), &ctx.block.deferred) == -1){
	if(s_expr(c3nodechild(*ctx.c3, index, 0), &ctx.block.deferred) == -1){
		ERROR("Failed to analyze deferred expression", 0);
		return -1;
	}


@@ 67,20 68,20 @@ s_binop(uint32_t index, c3tag tag)
{
	uint32_t lhs, rhs;
	uint32_t olhs, orhs;
	olhs = c3nodechild(*ctx.tree, index, 0);
	orhs = c3nodechild(*ctx.tree, index, 1);
	if(c3nodetag(*ctx.tree, olhs) == C3_VAR || c3nodetag(*ctx.tree, olhs) == C3_CONST)
	olhs = c3nodechild(*ctx.c3, index, 0);
	orhs = c3nodechild(*ctx.c3, index, 1);
	if(c3nodetag(*ctx.c3, olhs) == C3_VAR || c3nodetag(*ctx.c3, olhs) == C3_CONST)
		lhs = olhs;
	else
		lhs = s_expr(olhs, &ctx.block.stmts);
	rhs = s_expr(orhs, &ctx.block.stmts);
	if(c3nodetag(*ctx.tree, olhs) == C3_DISCARD && tag == C3_IR_SET)
	if(c3nodetag(*ctx.c3, olhs) == C3_DISCARD && tag == C3_IR_SET)
		return 0;
	if(lhs == -1 || rhs == -1){
		ERROR("Failed to analyze binop '%s' subexprs", c3tagstr(tag));
		return -1;
	}
	index = c3append(ctx.tree, tag, 2, lhs, rhs);
	index = c3append(ctx.c3, tag, 2, lhs, rhs);
	if(index == -1)
		OOM();
	return index;


@@ 94,16 95,16 @@ s_scope_args(uint32_t index)
	uint32_t params;
	uint32_t args;
	uint32_t arg;
	params = c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, ctx.cur_fn, 0), 1);
	args = c3nodechild(*ctx.tree, index, 1);
	count = c3nodekids(*ctx.tree, params);
	if(count != c3nodekids(*ctx.tree, args)){
		ERROR("Args and params count differ: %d vs %d!", count, c3nodekids(*ctx.tree, args));
	params = c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, ctx.cur_fn, 0), 1);
	args = c3nodechild(*ctx.c3, index, 1);
	count = c3nodekids(*ctx.c3, params);
	if(count != c3nodekids(*ctx.c3, args)){
		ERROR("Args and params count differ: %d vs %d!", count, c3nodekids(*ctx.c3, args));
		return 0;
	}
	for(i = 0; i < count; i += 1){
		WARN("TODO: validate inlined arg types", 0);
		arg = c3nodechild(*ctx.tree, args, i);
		arg = c3nodechild(*ctx.c3, args, i);
		if(stb_sb_push_u32(&ctx.inlined_args, arg) == -1)
			OOM();
	}


@@ 119,15 120,15 @@ s_inline(uint32_t index)
	uint32_t block;
	prev_fn = ctx.cur_fn;
	prev_args = ctx.inlined_args;
	fn = c3nodechild(*ctx.tree, index, 0);
	block = c3nodechild(*ctx.tree, fn, 1);
	fn = c3nodechild(*ctx.c3, index, 0);
	block = c3nodechild(*ctx.c3, fn, 1);
	ctx.inlined_args = NULL;
	ctx.cur_fn = fn;
	if(!s_scope_args(index)){
		ERROR("Failed to inject args into inline scope",0);
		return -1;
	}
	block = l_block(ctx.tree, block, 2);
	block = l_block(block, 2);
	ctx.cur_fn = prev_fn;
	stb_sb_free(ctx.inlined_args);
	ctx.inlined_args = prev_args;


@@ 149,15 150,15 @@ s_call(uint32_t index)
	uint16_t conv;
	kids = NULL;
	tmp = index;
	index = c3nodechild(*ctx.tree, index, 0);
	tag = c3nodetag(*ctx.tree, index);
	index = c3nodechild(*ctx.c3, index, 0);
	tag = c3nodetag(*ctx.c3, index);
	if(tag != C3_VALUE_FN){
		ERROR("ICE: expected VALUE_FN for EXPR_CALL, found '%s'", c3tagstr(tag));
		return -1;
	}
	param_list = c3nodechild(*ctx.tree, index, 0);
	conv = c3nodetag(*ctx.tree, c3nodechild(*ctx.tree, param_list, 2));
	param_list = c3nodechild(*ctx.tree, param_list, 1);
	param_list = c3nodechild(*ctx.c3, index, 0);
	conv = c3nodetag(*ctx.c3, c3nodechild(*ctx.c3, param_list, 2));
	param_list = c3nodechild(*ctx.c3, param_list, 1);
	if(conv == C3_CALL_CONV_INLINE)
		return s_inline(tmp);
	if(conv != C3_CALL_CONV_DEFAULT){


@@ 166,12 167,12 @@ s_call(uint32_t index)
	}
	if(!stb_sb_push_u32(&kids, index))
		OOM();
	index = c3nodechild(*ctx.tree, tmp, 1);
	count = c3nodekids(*ctx.tree, index);
	index = c3nodechild(*ctx.c3, tmp, 1);
	count = c3nodekids(*ctx.c3, index);
	for(i = 0; i < count; i += 1){
		tmp = c3nodechild(*ctx.tree, index, i);
		tmp = c3nodechild(*ctx.c3, index, i);
		tmp = s_expr(tmp, &ctx.block.stmts);
		tag = c3nodetag(*ctx.tree, c3nodechild(*ctx.tree, param_list, i));
		tag = c3nodetag(*ctx.c3, c3nodechild(*ctx.c3, param_list, i));
		if(tag == C3_COMPTIME){
			ERROR("TODO: comptime engine", 0);
			return -1;


@@ 183,7 184,7 @@ s_call(uint32_t index)
		if(!stb_sb_push_u32(&kids, tmp))
			OOM();
	}
	tmp = c3appendarr(ctx.tree, C3_IR_CALL, stb_sb_count(kids), kids);
	tmp = c3appendarr(ctx.c3, C3_IR_CALL, stb_sb_count(kids), kids);
	if(tmp == -1 || !stb_sb_push_u32(&ctx.fn_refs, tmp + 2))
		OOM();
	return tmp;


@@ 193,12 194,12 @@ static uint32_t
s_break(uint32_t index, c3tag tag)
{
	uint32_t val;
	val = s_expr(c3nodechild(*ctx.tree, index, 1), &ctx.block.stmts);
	val = s_expr(c3nodechild(*ctx.c3, index, 1), &ctx.block.stmts);
	if(val == -1){
		ERROR("Failed to analyze break value", 0);
		return -1;
	}
	val = c3append(ctx.tree, tag, 2, ctx.tree->nodes[index + 1], val);
	val = c3append(ctx.c3, tag, 2, ctx.c3->ast.nodes[index + 1], val);
	if(val == -1)
		OOM();
	return val;


@@ 209,11 210,11 @@ s_asm(uint32_t index)
{
	uint32_t child;
	uint32_t i, count;
	count = c3nodekids(*ctx.tree, index);
	count = c3nodekids(*ctx.c3, index);
	for(i = 1; i < count; i += 1){
		child = c3nodechild(*ctx.tree, index, i);
		ctx.tree->nodes[index + 1 + i] = s_expr(child, &ctx.block.stmts);
		if(ctx.tree->nodes[index + 1 + i] == -1){
		child = c3nodechild(*ctx.c3, index, i);
		ctx.c3->ast.nodes[index + 1 + i].node_index = s_expr(child, &ctx.block.stmts);
		if(ctx.c3->ast.nodes[index + 1 + i].node_index == -1){
			ERROR("Failed to analyze asm parameter", 0);
			return -1;
		}


@@ 224,7 225,7 @@ s_asm(uint32_t index)
static uint32_t
s_size(uint32_t index)
{
	c3tag tag = c3nodetag(*ctx.tree, index);
	c3tag tag = c3nodetag(*ctx.c3, index);
	switch(tag){
	case C3_TYPE_U32:
		return 4;


@@ 248,8 249,8 @@ s_initialized(uint32_t index)
	struct ctx_block old;
	uint32_t block;
	c3tag tag;
	type = c3nodechild(*ctx.tree, index, 0);
	init = c3nodechild(*ctx.tree, index, 1);
	type = c3nodechild(*ctx.c3, index, 0);
	init = c3nodechild(*ctx.c3, index, 1);
	checked_inits = NULL;
	offset = 0;
	old = ctx.block;


@@ 257,52 258,52 @@ s_initialized(uint32_t index)
	ctx.block.deferred = NULL;
	ctx.block.id = ctx.next_block_id;
	ctx.next_block_id += 1;
	for(i = 0; i < c3nodekids(*ctx.tree, init); i += 1){
		if(!stb_sb_push_u32(&checked_inits, c3nodechild(*ctx.tree, init, i)))
	for(i = 0; i < c3nodekids(*ctx.c3, init); i += 1){
		if(!stb_sb_push_u32(&checked_inits, c3nodechild(*ctx.c3, init, i)))
			OOM();
		if(checked_inits[i] == -1){
			ERROR("ICE: invalid initializer", 0);
			return -1;
		}
	}
	tag = c3nodetag(*ctx.tree, type);
	tag = c3nodetag(*ctx.c3, type);
	if(tag != C3_STRUCT){
		ERROR("Expected struct for initializer expr, found %s", c3tagstr(tag));
		return -1;
	}
	container = c3append(ctx.tree, C3_IR_CONTAINER, 1, index);
	container = c3append(ctx.c3, C3_IR_CONTAINER, 1, index);
	if(container == -1 || !stb_sb_push_u32(&ctx.block.stmts, container))
		OOM();
	for(i = 0; i < c3nodekids(*ctx.tree, type); i += 1){
		field = c3nodechild(*ctx.tree, type, i);
		tag = c3nodetag(*ctx.tree, field);
	for(i = 0; i < c3nodekids(*ctx.c3, type); i += 1){
		field = c3nodechild(*ctx.c3, type, i);
		tag = c3nodetag(*ctx.c3, field);
		if(tag != C3_FIELD){
			ERROR("TODO: validate tag %s", c3tagstr(tag));
			continue;
		}
		for(j = 0; j < stb_sb_count(checked_inits); j += 1){
			init = checked_inits[j];
			if(c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, init, 0), 0) == c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, field, 0), 0)){
			if(c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, init, 0), 0) == c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, field, 0), 0)){
				checked_inits[j] = -1;
				if(offset != 0){
					ERROR("TODO: multi-member containers", 0);
					return -1;
				}
				tmp = c3append(ctx.tree, C3_CONTAINER_OFFSET, 2, offset, container);
				tmp = c3append(ctx.c3, C3_CONTAINER_OFFSET, 2, offset, container);
				if(tmp == -1)
					OOM();
				tmp2 = s_size(c3nodechild(*ctx.tree, field, 1));
				tmp2 = s_size(c3nodechild(*ctx.c3, field, 1));
				if(tmp2 == -1){
					ERROR("ICE: unable to size member", 0);
					return -1;
				}
				offset += tmp2;
				tmp2 = s_expr(c3nodechild(*ctx.tree, init, 1), &ctx.block.stmts);
				tmp2 = s_expr(c3nodechild(*ctx.c3, init, 1), &ctx.block.stmts);
				if(tmp2 == -1 || tmp2 == 0){
					ERROR("Failed to lower IR for field init", 0);
					return -1;
				}
				tmp = c3append(ctx.tree, C3_IR_SET, 2, tmp, tmp2);
				tmp = c3append(ctx.c3, C3_IR_SET, 2, tmp, tmp2);
				if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
					OOM();
				j = -2;


@@ 311,13 312,13 @@ s_initialized(uint32_t index)
		}
		if(j == -2)
			continue;
		ERROR("Initializer for '%s' not found!", h_c3str(*ctx.tree, c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, field, 0), 0)));
		ERROR("Initializer for '%s' not found!", h_c3str(*ctx.c3, c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, field, 0), 0)));
		return -1;
	}
	tmp = c3append(ctx.tree, C3_IR_BREAK, 2, ctx.block.id, container);
	tmp = c3append(ctx.c3, C3_IR_BREAK, 2, ctx.block.id, container);
	if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
		OOM();
	block = c3appendarr(ctx.tree, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
	block = c3appendarr(ctx.c3, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
	stb_sb_free(ctx.block.stmts);
	if(ctx.block.deferred != NULL){
		ERROR("ICE: deferred statements should not show up within initializers", 0);


@@ 339,22 340,22 @@ s_if(uint32_t index)
		ctx.block.stmts = NULL;
		ctx.block.id = ctx.next_block_id;
		ctx.next_block_id += 1;
		tmp = s_expr(c3nodechild(*ctx.tree, index, 0), &ctx.block.stmts);
		tmp = s_expr(c3nodechild(*ctx.c3, index, 0), &ctx.block.stmts);
		if(tmp == -1){
			ERROR("Failed to analyze loop condition", 0);
			ctx.block = old;
			return -1;
		}
		tmp = c3append(ctx.tree, C3_IR_INVERT, 1, tmp);
		tmp = c3append(ctx.c3, C3_IR_INVERT, 1, tmp);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		tmp = c3append(ctx.tree, C3_IR_COND_BR, 1, tmp);
		tmp = c3append(ctx.c3, C3_IR_COND_BR, 1, tmp);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		tmp = l_block(ctx.tree, c3nodechild(*ctx.tree, index, 1), 0);
		tmp = l_block(c3nodechild(*ctx.c3, index, 1), 0);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		block = c3appendarr(ctx.tree, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
		block = c3appendarr(ctx.c3, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
		stb_sb_free(ctx.block.stmts);
		ctx.block = old;
		return block;


@@ 367,18 368,18 @@ s_param(uint32_t index)
	c3tag tag;
	uint32_t node, param;
	// valuefn -> typefn
	node = c3nodechild(*ctx.tree, ctx.cur_fn, 0);
	node = c3nodechild(*ctx.c3, ctx.cur_fn, 0);
	// typefn -> paramlist
	node = c3nodechild(*ctx.tree, node, 1);
	for(i = 0; i < c3nodekids(*ctx.tree, node); i += 1){
		param = c3nodechild(*ctx.tree, node, i);
		tag = c3nodetag(*ctx.tree, param);
	node = c3nodechild(*ctx.c3, node, 1);
	for(i = 0; i < c3nodekids(*ctx.c3, node); i += 1){
		param = c3nodechild(*ctx.c3, node, i);
		tag = c3nodetag(*ctx.c3, param);
		if(tag == C3_COMPTIME)
			param = c3nodechild(*ctx.tree, param, 0);
			param = c3nodechild(*ctx.c3, param, 0);
		if(param == index){
			if(ctx.inlined_args)
				return ctx.inlined_args[i];
			return c3append(ctx.tree, C3_IR_ARG, 1, i);
			return c3append(ctx.c3, C3_IR_ARG, 1, i);
		}
	}
	ERROR("ICE: Param not found", 0);


@@ 388,17 389,17 @@ s_param(uint32_t index)
static uint32_t
s_panic(uint32_t index)
{
	uint32_t arg = c3nodechild(*ctx.tree, index, 0);
	if(c3nodekids(*ctx.tree, arg) != 1){
	uint32_t arg = c3nodechild(*ctx.c3, index, 0);
	if(c3nodekids(*ctx.c3, arg) != 1){
		ERROR("Expected 1 argument to @panic!", 0);
		return -1;
	};
	arg = c3nodechild(*ctx.tree, arg, 0);
	if(c3nodetag(*ctx.tree, arg) != C3_LITERAL_STRING){
	arg = c3nodechild(*ctx.c3, arg, 0);
	if(c3nodetag(*ctx.c3, arg) != C3_LITERAL_STRING){
		ERROR("Expected string literal argument to @panic!", 0);
		return -1;
	}
	return c3append(ctx.tree, C3_IR_PANIC, 1, arg);
	return c3append(ctx.c3, C3_IR_PANIC, 1, arg);
}

// if list is non-null, and analysis succeeds, the IR for the resulting expression is appended given insn list


@@ 408,7 409,7 @@ s_expr(uint32_t index, uint32_t **list)
{
	uint32_t tmp;
	uint32_t ret;
	c3tag tag = c3nodetag(*ctx.tree, index);
	c3tag tag = c3nodetag(*ctx.c3, index);
	switch(tag){
	case C3_LOOP:{
		uint32_t block;


@@ 417,25 418,25 @@ s_expr(uint32_t index, uint32_t **list)
		ctx.block.stmts = NULL;
		ctx.block.id = ctx.next_block_id;
		ctx.next_block_id += 1;
		tmp = s_expr(c3nodechild(*ctx.tree, index, 0), &ctx.block.stmts);
		tmp = s_expr(c3nodechild(*ctx.c3, index, 0), &ctx.block.stmts);
		if(tmp == -1){
			ERROR("Failed to analyze loop condition", 0);
			ctx.block = old;
			return -1;
		}
		tmp = c3append(ctx.tree, C3_IR_INVERT, 1, tmp);
		tmp = c3append(ctx.c3, C3_IR_INVERT, 1, tmp);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		tmp = c3append(ctx.tree, C3_IR_COND_BR, 1, tmp);
		tmp = c3append(ctx.c3, C3_IR_COND_BR, 1, tmp);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		tmp = l_block(ctx.tree, c3nodechild(*ctx.tree, index, 1), 0);
		tmp = l_block(c3nodechild(*ctx.c3, index, 1), 0);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		tmp = c3append(ctx.tree, C3_IR_RELOOP, 0);
		tmp = c3append(ctx.c3, C3_IR_RELOOP, 0);
		if(tmp == -1 || !stb_sb_push_u32(&ctx.block.stmts, tmp))
			OOM();
		block = c3appendarr(ctx.tree, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
		block = c3appendarr(ctx.c3, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
		stb_sb_free(ctx.block.stmts);
		ctx.block = old;
		ret = block;


@@ 483,7 484,7 @@ s_expr(uint32_t index, uint32_t **list)
			ERROR("Failed to analyze %s", c3tagstr(tag));
		if(!stb_sb_push_u32(list, ret))
			OOM();
		ret = c3append(ctx.tree, C3_IR_SET, 2, c3nodechild(*ctx.tree, ret, 0), ret);
		ret = c3append(ctx.c3, C3_IR_SET, 2, c3nodechild(*ctx.c3, ret, 0), ret);
		break;
	case C3_EXPR_BITWISE_OR:
		ret = s_binop(index, C3_IR_BITWISE_OR);


@@ 552,7 553,7 @@ s_stmt(uint32_t index)

//types: 0 normal, 1 root of fn, 2 inline
static uint32_t
l_block(struct c3tree *tree, uint32_t index, int type)
l_block(uint32_t index, int type)
{
	uint32_t i, j;
	uint32_t block;


@@ 566,38 567,38 @@ l_block(struct c3tree *tree, uint32_t index, int type)
	ctx.block.id = ctx.next_block_id;
	ctx.next_block_id += 1;
	if(type == 1){
		uint32_t type = c3nodechild(*ctx.tree, ctx.cur_fn, 0);
		if(c3nodetag(*ctx.tree, type) == C3_VOID){
			i = c3append(ctx.tree, C3_IR_RETURN, 0);
		uint32_t type = c3nodechild(*ctx.c3, ctx.cur_fn, 0);
		if(c3nodetag(*ctx.c3, type) == C3_VOID){
			i = c3append(ctx.c3, C3_IR_RETURN, 0);
			if(i == -1 || !stb_sb_push_u32(&ctx.block.deferred, i))
				OOM();
		}
	}
	tag = c3nodetag(*ctx.tree, index);
	tag = c3nodetag(*ctx.c3, index);
	if(tag != C3_BLOCK){
		ERROR("Expected block, found %s", c3tagstr(tag));
		abort();
		return -1;
	}
	for(i = 1; i < c3nodekids(*ctx.tree, index); i += 1){
		if(c3nodetag(*ctx.tree, c3nodechild(*ctx.tree, index, i)) == C3_EXPR_RETURN && type == 2){
			j = s_expr(c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, index, i), 1), &ctx.block.stmts);
	for(i = 1; i < c3nodekids(*ctx.c3, index); i += 1){
		if(c3nodetag(*ctx.c3, c3nodechild(*ctx.c3, index, i)) == C3_EXPR_RETURN && type == 2){
			j = s_expr(c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, index, i), 1), &ctx.block.stmts);
			if(j == -1){
				ERROR("Failed to analyze inline return expr", 0);
				return -1;
			}
			j = c3append(ctx.tree, C3_IR_BREAK, 2, ctx.block.id, j);
			j = c3append(ctx.c3, C3_IR_BREAK, 2, ctx.block.id, j);
			if(j == -1 || !stb_sb_push_u32(&ctx.block.stmts, j))
				OOM();
			continue;
		}
		if(!s_stmt(tree->nodes[index + 1 + i])){
			ERROR("Failed to lower statement %s", c3tagstr(c3nodetag(*ctx.tree, ctx.tree->nodes[index + 1 + i])));
		if(!s_stmt(ctx.c3->ast.nodes[index + 1 + i].node_index)){
			ERROR("Failed to lower statement %s", c3tagstr(c3nodetag(*ctx.c3, ctx.c3->ast.nodes[index + 1 + i].node_index)));
			return -1;
		}
	}
	l_inject_deferred();
	block = c3appendarr(tree, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
	block = c3appendarr(ctx.c3, C3_BLOCK, stb_sb_count(ctx.block.stmts), ctx.block.stmts);
	stb_sb_free(ctx.block.stmts);
	stb_sb_free(ctx.block.deferred);
	ctx.block = old;


@@ 610,15 611,15 @@ static uint32_t
s_decl(uint32_t index)
{
	uint32_t tmp;
	tmp = s_expr(c3nodechild(*ctx.tree, index, 2), &ctx.block.stmts);
	tmp = s_expr(c3nodechild(*ctx.c3, index, 2), &ctx.block.stmts);
	if(tmp == -1)
		return -1;
	if(tmp == 0)
		return 0;
	// Replace the value with the new one
	ctx.tree->nodes[index + 3] = tmp;
	if(c3nodetag(*ctx.tree, index) == C3_VAR){
		index = c3append(ctx.tree, C3_IR_SET, 2, index, tmp);
	ctx.c3->ast.nodes[index + 3].node_index = tmp;
	if(c3nodetag(*ctx.c3, index) == C3_VAR){
		index = c3append(ctx.c3, C3_IR_SET, 2, index, tmp);
		if(index == -1){
			ERROR("ICE: OOM", 0);
			return -1;


@@ 636,12 637,12 @@ l_fn(uint32_t index)
	ctx.cur_fn = index;
	if(!stb_sb_push_u32(&ctx.fn_map.values, index))
		OOM();
	tmp = l_block(ctx.tree, c3nodechild(*ctx.tree, index, 1), 1);
	tmp = l_block(c3nodechild(*ctx.c3, index, 1), 1);
	if(tmp == -1){
		ERROR("Failed to lower function block", 0);
		return -1;
	}
	index = c3append(ctx.tree, C3_IR_FN, 2, tmp, index);
	index = c3append(ctx.c3, C3_IR_FN, 2, tmp, index);
	if(!stb_sb_push_u32(&ctx.fn_map.irs, index))
		OOM();
	return index;


@@ 652,58 653,47 @@ expr_gen_test(void)
{
	uint32_t *kids = NULL;
	uint32_t i;
	uint32_t test;
	uint32_t call;
	uint16_t count;
	uint32_t args = c3append(ctx.tree, C3_EXPR_LIST,0);
	uint32_t block, fn, test_runner;
	uint32_t block;
	uint32_t args = c3append(ctx.c3, C3_EXPR_LIST,0);
	uint32_t rettype, callconv, param_list, fntype;
	uint32_t main = c3intern(ctx.tree, "main", 4);
	main = c3append(ctx.tree, C3_IDENT, 1, main);
	uint32_t main = c3intern(ctx.c3, "main", 4);
	main = c3append(ctx.c3, C3_IDENT, 1, main);
	rettype = t_dedup_tag(C3_VOID);
	callconv = t_dedup_tag(C3_CALL_CONV_INLINE);
	param_list = c3append(ctx.tree, C3_FN_PARAM_LIST, 0);
	param_list = c3append(ctx.c3, C3_FN_PARAM_LIST, 0);
	if(rettype == -1 || callconv == -1 || param_list == -1 || args == -1)
	OOM();
	fntype = c3append(ctx.tree, C3_TYPE_FN, 3, rettype, param_list, callconv);
	fntype = c3append(ctx.c3, C3_TYPE_FN, 3, rettype, param_list, callconv);
	if(!stb_sb_push_u32(&kids, rettype) || fntype == -1 )
		OOM();
	uint32_t tests = ctx.tree->graphs.tests;
	for(i = 0; i < c3nodekids(*ctx.tree, tests); i += 1){
		test = c3nodechild(*ctx.tree, tests, i);
		call = c3append(ctx.tree, C3_EXPR_CALL, 2, c3nodechild(*ctx.tree, test, 0), args);
	uint32_t *tests = ctx.c3->analysis.tests;
	for(i = 0; i < stb_sb_count(tests); i += 1){
		call = c3append(ctx.c3, C3_EXPR_CALL, 2, c3nodechild(*ctx.c3, tests[i], 0), args);
		if(call == -1 || !stb_sb_push_u32(&kids, call))
			OOM();
	}
	block = c3appendarr(ctx.tree, C3_BLOCK, stb_sb_count(kids), kids);
	block = c3appendarr(ctx.c3, C3_BLOCK, stb_sb_count(kids), kids);
	if(block == -1)
		OOM();
	fn = c3append(ctx.tree, C3_VALUE_FN, 3, fntype, block, main);
	call = ctx.tree->graphs.call;
	count = c3nodekids(*ctx.tree, call);
	if(ctx.tree->nodes[call + count] != -1){
		ERROR("ICE: reserved spot not empty", 0);
		return 0;
	}
	test_runner = c3append(ctx.tree, C3_DEP, 1, fn);
	ctx.tree->nodes[call + count] = test_runner;
	return l_fn(fn);
	ERROR("TODO: reimplement test generation", 0);
	return 0;
}

static void
ctx_init(struct c3tree *tree)
ctx_init(c3ctx *c3)
{
	ctx.fn_refs = NULL;
	ctx.fn_map.irs = NULL;
	ctx.fn_map.values = NULL;
	ctx.tree = tree;
	ctx.c3 = c3;
	ctx.inlined_args = NULL;
}

int
expr_init(struct c3tree *tree)
expr_init(c3ctx *c3)
{
	ctx_init(tree);
	ctx_init(c3);
	return 1;
}



@@ 713,25 703,28 @@ expr_finish(void)
	uint32_t i;
	uint32_t index, rindex;
	uint16_t kids;
	uint32_t root;
	root = ctx.tree->graphs.call;
	kids = c3nodekids(*ctx.tree, root);
	for(i = 0; i < kids - 1; i += 1){
		index = c3nodechild(*ctx.tree, root, i);
		rindex = s_map(c3nodechild(*ctx.tree, index, 0));
	uint32_t *fns = ctx.c3->analysis.fns;
	if(fns == NULL){
		ERROR("Function list not found!", 0);
		return 0;
	}
	kids = stb_sb_count(fns);
	for(i = 0; i < kids; i += 1){
		index = fns[i];
		rindex = s_map(c3nodechild(*ctx.c3, index, 0));
		if(rindex == -1){
			ERROR("ICE: unable to map function from CFG to IR; index %u: %u", index, c3nodechild(*ctx.tree, index, 0));
			c3nodedump(*ctx.tree, rindex, 0, 1);
			ERROR("ICE: unable to map function from CFG to IR; index %u: %u", index, c3nodechild(*ctx.c3, index, 0));
			c3nodedump(*ctx.c3, rindex, 0, 1);
			return 0;
		}
		ctx.tree->nodes[index + 1] = rindex;
		ctx.c3->ast.nodes[index + 1].node_index = rindex;
	}
	for(i = 0; i < stb_sb_count(ctx.fn_refs); i += 1){
		rindex = ctx.fn_refs[i];
		index = ctx.tree->nodes[rindex];
		ctx.tree->nodes[index] = s_map(index);
		if(ctx.tree->nodes[index] == -1){
			ERROR("ICE: unable to map function from child index %d to IR", ctx.tree->nodes[index]);
		index = ctx.c3->ast.nodes[rindex].node_index;
		ctx.c3->ast.nodes[index].node_index = s_map(index);
		if(ctx.c3->ast.nodes[index].node_index == -1){
			ERROR("ICE: unable to map function from child index %d to IR", ctx.c3->ast.nodes[index]);
			return 0;
		}
	}

M sema/scope.c => sema/scope.c +9 -9
@@ 4,7 4,7 @@

#include "../util.h"
#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../stb.h"
#include "../stb_ds.h"
#include "scope.h"


@@ 24,10 24,10 @@ struct c3scope{
};

static struct{
	struct c3tree *tree;
	c3ctx *c3;
	struct c3scope *scopes;
	struct{
		struct c3tree *key;
		c3ctx *key;
		struct c3scope *value;
	}map;
} ctx;


@@ 72,13 72,13 @@ scope_find_decl(uint32_t scope_handle, uint32_t ident, uint32_t *parent, int onl
	c3tag tag;
	scope = &ctx.scopes[scope_handle];
	for(i = 0; i < stbds_hmlenu(scope->values); i += 1){
		tag = c3nodetag(*ctx.tree, scope->values[i].key);
		tag = c3nodetag(*ctx.c3, scope->values[i].key);
		if(tag != C3_CONST && tag != C3_FN_PARAM && tag != C3_VAR)
			continue;
		if(scope->values[i].value == 0 && only_analyzed)
			continue;
		t_ident = c3nodechild(*ctx.tree, scope->values[i].key, 0);
		t_ident = c3nodechild(*ctx.tree, t_ident, 0);
		t_ident = c3nodechild(*ctx.c3, scope->values[i].key, 0);
		t_ident = c3nodechild(*ctx.c3, t_ident, 0);
		if(t_ident == ident){
			if(parent != NULL)
				*parent = scope_handle;


@@ 123,9 123,9 @@ scope_insert(uint32_t scope, uint32_t value)
}

void
scope_init(struct c3tree *tree)
scope_init(c3ctx *c3ctx)
{
	ctx.tree = tree;
	ctx.c3 = c3ctx;
}

void


@@ 141,5 141,5 @@ scope_free(void)
	}
	stb_sb_free(ctx.scopes);
	ctx.scopes = NULL;
	ctx.tree = NULL;
	ctx.c3 = NULL;
}

M sema/scope.h => sema/scope.h +1 -1
@@ 4,7 4,7 @@
// used!

// Initializes scope tracking. Call this before doing anything else.
void scope_init(struct c3tree *tree);
void scope_init(c3ctx *);
// Creates a new scope as a subscope of the specified parent. If node is -1,
// there is no way to look up the subscope from the parent. Otherwise, if an
// identifier is associated with the node, it is possible to find the subscope

M sema/serialize.c => sema/serialize.c +4 -53
@@ 4,7 4,7 @@

#include "../tags.h"
#include "../util.h"
#include "../tree.h"
#include "../c3.h"
#include "../stb.h"

/*


@@ 15,62 15,13 @@

static struct{
	uint32_t *list;
	struct c3tree *tree;
	c3ctx *c3;
} ctx;

static int
in_list(uint32_t index)
{
	uint32_t i;
	for(i = 0; i < stb_sb_count(ctx.list); i += 1)
		if(ctx.list[i] == index)
			return 1;
	return 0;
}

static int
serialize_node(uint32_t index)
{
	uint32_t i;
	uint16_t kids = c3nodekids(*ctx.tree, index);
	if(in_list(index))
		return 1;
	for(i = 1; i < kids; i += 1){
		if(!serialize_node(c3nodechild(*ctx.tree, index, i)))
			return 0;
	};
	if(c3nodechild(*ctx.tree, index, 0) != 0 && !stb_sb_push_u32(&ctx.list, index))
		return 0;
	return 1;
}

static int
dep_compare(const void *a, const void *b)
{
	const uint32_t *i1 = a;
	const uint32_t *i2 = b;
	uint16_t kids1 = c3nodekids(*ctx.tree, *i1);
	uint16_t kids2 = c3nodekids(*ctx.tree, *i2);
	if(kids1 == 0){
		if(kids2 == 0)
			return 0;
		return -1;
	}
	else if(kids2 == 0)
		return 1;
	return 0;
}

int
serialize_deps(struct c3tree *tree)
serialize_deps(c3ctx *c3ctx)
{
	ctx.tree = tree;
	ctx.c3 = c3ctx;
	ctx.list = NULL;
	if(!serialize_node(tree->graphs.deps))
		return 0;
	// At this point, we have a unique list. Since each node also contains 
	// its deps, we can just do a brute-force sort
	qsort(ctx.list, stb_sb_count(ctx.list), sizeof(uint32_t), dep_compare);
//	tree->graphs.deps = c3appendarr(tree, (c3tag tag, uint16_t kid_count){C3_DEP_NODE, stb_sb_count(ctx.list), 0}, ctx.list);
	return 1;
}

M sema/type.c => sema/type.c +11 -41
@@ 5,33 5,23 @@
#include <string.h>

#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../util.h"
#include "../stb.h"
#include "../stb_ds.h"

static struct{
	struct c3tree *tree;
	//deduplicated type list inherited from the parser
	uint32_t *list;
	struct{
		// node index
		uint32_t key;
		// type
		uint32_t value;
	} *types;
}ctx;
static c3ctx *ctx;

uint32_t
t_dedup_tag(uint32_t tag)
{
	uint32_t i;
	for(i = 0; i < stb_sb_count(ctx.list); i += 1)
		if(c3nodetag(*ctx.tree, ctx.list[i]) == tag)
			return ctx.list[i];
	for(i = 0; i < stb_sb_count(ctx->dedup.types); i += 1)
		if(c3nodetag(*ctx, ctx->dedup.types[i]) == tag)
			return ctx->dedup.types[i];
	// Not yet present!
	i = c3append(ctx.tree, tag, 0);
	if(i == -1 || !stb_sb_push_u32(&ctx.list, i))
	i = c3append(ctx, tag, 0);
	if(i == -1 || !stb_sb_push_u32(&ctx->dedup.types, i))
		OOM();
	return i;
}


@@ 39,34 29,14 @@ t_dedup_tag(uint32_t tag)
uint32_t
t_type(uint32_t index)
{
	return stbds_hmget(ctx.types, index);
}

static int
t_inherit(void)
{
	uint32_t i;
	uint32_t count;
	if(ctx.tree->graphs.type == -1){
		ERROR("ICE: Typification depends on the deduplicated type list!", 0);
		return 0;
	}
	count = c3nodekids(*ctx.tree, ctx.tree->graphs.type);
	if(!stb_sb_ensure_capacity(&ctx.list, count, sizeof(uint32_t)))
		OOM();
	for(i = 0; i < count; i += 1)
		ctx.list[i] = c3nodechild(*ctx.tree, ctx.tree->graphs.type, i);
	stb_sb_increment(ctx.list, count);
	return 1;
	return stbds_hmget(ctx->analysis.types, index);
}

int
t_init(struct c3tree *tree)
t_init(c3ctx *c3ctx)
{
	ctx.tree = tree;
	ctx.list = NULL;
	ctx.types = NULL;
	return t_inherit();
	ctx = c3ctx;
	return 1;
}

int

M sema/usage.c => sema/usage.c +80 -105
@@ 4,35 4,23 @@

#include "../util.h"
#include "../tags.h"
#include "../tree.h"
#include "../c3.h"
#include "../stb.h"
#include "../stb_ds.h"
#include "scope.h"

static struct ctx {
	struct c3tree *tree;
	c3ctx *c3;
	struct{
		// top of the graph. Exported functions are *all* tops, as each one
		// provides an external entry point which may be invoked
		uint32_t *top;
		// list of nodes used by the node being analyzed
		// This value exists per-node, so it must be saved before descending into a node, and restored afterwards
		// these children are themselves call graph nodes, not main tree nodes!
		// these children are themselves call graph nodes, not main ctx nodes!
		uint32_t *children;
	} call_graph;
	struct{
		// All analyzed nodes are in here
		// nodes in the midst of analysis are in here with value -1
		struct{
			// tree node index
			uint32_t key;
			// dep node index
			uint32_t value;
		} *analyzed;
		uint32_t *top;
		// list of nodes used by the node being analyzed
		// This value exists per-node, so it must be saved before descending into a node, and restored afterwards
		// these children are themselves dep tree nodes, not main tree nodes!
		// these children are themselves dep ctx nodes, not main ctx nodes!
		uint32_t *children;
	} deps;
	uint32_t *tests;


@@ 47,7 35,7 @@ u_find(uint32_t **list, uint32_t index)
{
	uint32_t i;
	for(i = 0; i < stb_sb_count(*list); i += 1)
		if(c3nodechild(*ctx.tree, (*list)[i], 0) == index)
		if(c3nodechild(*ctx.c3, (*list)[i], 0) == index)
			return (*list)[i];
	return -1;
}


@@ 61,8 49,8 @@ uint32_t t_dedup_tag(uint32_t tag);
static int
u_type(uint32_t source)
{
	uint32_t index = ctx.tree->nodes[source];
	if(c3nodetag(*ctx.tree, index) != C3_TYPE_UNRES)
	uint32_t index = ctx.c3->ast.nodes[source].node_index;
	if(c3nodetag(*ctx.c3, index) != C3_TYPE_UNRES)
		return u_node(source, 1);
	return 1;
}


@@ 71,7 59,7 @@ static inline int
u_kids(uint32_t index, const char *msg)
{
	uint32_t i;
	uint16_t kid_count = c3nodekids(*ctx.tree, index);
	uint16_t kid_count = c3nodekids(*ctx.c3, index);
	for(i = 1; i < kid_count; i += 1)
		if(!u_node(index + 0 + i, 1)){
			ERROR("%s", msg);


@@ 104,13 92,13 @@ resolve_expr(uint32_t index, uint32_t containing_scope, uint32_t *parent_scope)
		ERROR("resolve_expr given bad node", 0);
		return -1;
	}
	tag = c3nodetag(*ctx.tree, index);
	tag = c3nodetag(*ctx.c3, index);
	if(tag == C3_IDENT){
		val = scope_find_decl(containing_scope, index, parent_scope, 0);
	}
	else if(tag == C3_EXPR_MEMBER){
		// lhs must be either ident or member, so just resolve_expr it
		lhs = resolve_expr(c3nodechild(*ctx.tree, index, 0), containing_scope, parent_scope);
		lhs = resolve_expr(c3nodechild(*ctx.c3, index, 0), containing_scope, parent_scope);
		if(lhs == -1 || *parent_scope == -1){
			ERROR("Failed to resolve member subexpr", 0);
			return -1;


@@ 125,7 113,7 @@ resolve_expr(uint32_t index, uint32_t containing_scope, uint32_t *parent_scope)
			}
		}
		*parent_scope = tmp;
		ident = c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, index, 1), 0);
		ident = c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, index, 1), 0);
		val = scope_find_decl(*parent_scope, ident, parent_scope, 0);
	}
	else{


@@ 134,18 122,18 @@ resolve_expr(uint32_t index, uint32_t containing_scope, uint32_t *parent_scope)
	}
	if(val == -1){
		ERROR("Unable to resolve decl", 0);
		c3nodedump(*ctx.tree, index, 0, 1);
		c3nodedump(*ctx.c3, index, 0, 1);
		puts("");
		return -1;
	}
	tag = c3nodetag(*ctx.tree, val);
	tag = c3nodetag(*ctx.c3, val);
	if(tag == C3_PUB){
		val = c3nodechild(*ctx.tree, val, 0);
		tag = c3nodetag(*ctx.tree, val);
		val = c3nodechild(*ctx.c3, val, 0);
		tag = c3nodetag(*ctx.c3, val);
	}
	if(tag == C3_CONST){
		val = c3nodechild(*ctx.tree, val, 2);
		tag = c3nodetag(*ctx.tree, val);
		val = c3nodechild(*ctx.c3, val, 2);
		tag = c3nodetag(*ctx.c3, val);
	}
	if(tag == C3_IDENT || tag == C3_EXPR_MEMBER)
		val = resolve_expr(val, *parent_scope, parent_scope);


@@ 166,14 154,14 @@ u_call(uint32_t source)
	uint32_t next_scope, prev_scope;
	uint16_t conv;
	uint32_t fn_src;
	uint32_t index = ctx.tree->nodes[source];
	uint32_t index = ctx.c3->ast.nodes[source].node_index;
	uint32_t kiddex;
	c3tag tag;
	next_scope = ctx.current_scope;
	if(c3nodetag(*ctx.tree, index) == C3_BUILTIN_PANIC)
		return u_fnargs(c3nodechild(*ctx.tree, index, 0));
	ctx.tree->nodes[index + 2] = resolve_expr(c3nodechild(*ctx.tree, index, 0), ctx.current_scope, &next_scope);
	if(ctx.tree->nodes[index + 2] == -1){
	if(c3nodetag(*ctx.c3, index) == C3_BUILTIN_PANIC)
		return u_fnargs(c3nodechild(*ctx.c3, index, 0));
	ctx.c3->ast.nodes[index + 2].node_index = resolve_expr(c3nodechild(*ctx.c3, index, 0), ctx.current_scope, &next_scope);
	if(ctx.c3->ast.nodes[index + 2].node_index == -1){
		ERROR("Failed to resolve expr", 0);
		return 0;
	}


@@ 181,14 169,14 @@ u_call(uint32_t source)
		ERROR("ICE: bad scope", 0);
		return 0;
	}
	kiddex = c3nodechild(*ctx.tree, index, 0);
	tag = c3nodetag(*ctx.tree, kiddex);
	kiddex = c3nodechild(*ctx.c3, index, 0);
	tag = c3nodetag(*ctx.c3, kiddex);
	if(tag != C3_VALUE_FN){
		ERROR("ICE: call target didn't resolve into function: %s", c3tagstr(tag));
		return 0;
	}
	fn_src = index + 1;
	conv = c3nodetag(*ctx.tree, c3nodechild(*ctx.tree, c3nodechild(*ctx.tree, kiddex, 0), 2));
	conv = c3nodetag(*ctx.c3, c3nodechild(*ctx.c3, c3nodechild(*ctx.c3, kiddex, 0), 2));
	prev_scope = ctx.current_scope;
	ctx.current_scope = next_scope;
	if(!u_node(fn_src, conv != C3_CALL_CONV_INLINE)){


@@ 197,7 185,7 @@ u_call(uint32_t source)
		return 0;
	}
	ctx.current_scope = prev_scope;
	return u_fnargs(c3nodechild(*ctx.tree, index, 1));
	return u_fnargs(c3nodechild(*ctx.c3, index, 1));
}

static int


@@ 206,8 194,8 @@ u_initialized(uint32_t index)
	uint32_t value;
	uint32_t i;
	uint16_t kidcount;
	value = c3nodechild(*ctx.tree, index, 1);
	kidcount = c3nodekids(*ctx.tree, value);
	value = c3nodechild(*ctx.c3, index, 1);
	kidcount = c3nodekids(*ctx.c3, value);
	if(!u_node(index + 1, 1)){
		ERROR("Failed to analyze type of initialized expression", 0);
		return 0;


@@ 230,7 218,7 @@ u_this(uint32_t source)
		ERROR("ICE: unable to find deepest container", 0);
		return 0;
	}
	ctx.tree->nodes[source] = ctx.deepest_container;
	ctx.c3->ast.nodes[source].node_index = ctx.deepest_container;
	return 1;
}



@@ 247,19 235,19 @@ u_scope_declarative_scope(uint32_t parent_scope, uint32_t index)
	uint32_t scope;
	uint32_t child;
	c3tag tag;
	uint16_t kids = c3nodekids(*ctx.tree, index);
	uint16_t kids = c3nodekids(*ctx.c3, index);
	scope = scope_new(parent_scope, index);
	if(scope == -1){
		ERROR("Failed to create declarative scope", 0);
		return -1;
	}
	for(i = 0; i < kids; i += 1){
		child = c3nodechild(*ctx.tree, index, i);
		tag = c3nodetag(*ctx.tree, child);
		child = c3nodechild(*ctx.c3, index, i);
		tag = c3nodetag(*ctx.c3, child);
		if(tag == C3_EXPORT)
			child = c3nodechild(*ctx.tree, child, 0);
			child = c3nodechild(*ctx.c3, child, 0);
		if(tag == C3_PUB)
			child = c3nodechild(*ctx.tree, child, 0);
			child = c3nodechild(*ctx.c3, child, 0);
		if(tag == C3_CONST || tag == C3_VAR)
			if(!scope_insert(scope, child))
				return -1;


@@ 273,7 261,7 @@ u_declarative_scope(uint32_t index)
	uint32_t i;
	uint32_t prev_scope = ctx.current_scope;
	uint32_t prev_cont = ctx.deepest_container;
	uint16_t kids = c3nodekids(*ctx.tree, index);
	uint16_t kids = c3nodekids(*ctx.c3, index);
	uint32_t child;
	c3tag tag;
	int ret = 1;


@@ 288,8 276,8 @@ u_declarative_scope(uint32_t index)
	}
	ctx.deepest_container = index;
	for(i = 0; ret == 1 && i < kids; i += 1){
		child = c3nodechild(*ctx.tree, index, i);
		tag = c3nodetag(*ctx.tree, child);
		child = c3nodechild(*ctx.c3, index, i);
		tag = c3nodetag(*ctx.c3, child);
		switch(tag){
		case C3_EXPORT:
			if(!u_node(index + 1 + i, 1)){


@@ 330,16 318,16 @@ u_test(uint32_t index)
	uint32_t fn, fntype, rettype, param_list, callconv;
	rettype = t_dedup_tag(C3_VOID);
	callconv = t_dedup_tag(C3_CALL_CONV_INLINE);
	param_list = c3append(ctx.tree, C3_FN_PARAM_LIST, 0);
	param_list = c3append(ctx.c3, C3_FN_PARAM_LIST, 0);
	if(rettype == -1 || callconv == -1 || param_list == -1)
		OOM();
	fntype = c3append(ctx.tree, C3_TYPE_FN, 3, rettype, param_list, callconv);
	fntype = c3append(ctx.c3, C3_TYPE_FN, 3, rettype, param_list, callconv);
	if(fntype == -1)
		OOM();
	fn = c3append(ctx.tree, C3_VALUE_FN, 4, index, fntype, c3nodechild(*ctx.tree, index, 0), -1);
	fn = c3append(ctx.c3, C3_VALUE_FN, 4, index, fntype, c3nodechild(*ctx.c3, index, 0), -1);
	if(fn == -1)
		OOM();
	ctx.tree->nodes[index + 1] = fn;
	ctx.c3->ast.nodes[index + 1].node_index = fn;
	if(!stb_sb_push_u32(&ctx.tests, index))
		return 0;
	return u_node(index + 1, 1);


@@ 348,14 336,14 @@ u_test(uint32_t index)
static int
u_return(uint32_t source)
{
	uint32_t index = ctx.tree->nodes[source];
	uint32_t retval = c3nodechild(*ctx.tree, index, 0);
	index = c3append(ctx.tree, C3_EXPR_RETURN, 2, ctx.current_fn, retval);
	uint32_t index = ctx.c3->ast.nodes[source].node_index;
	uint32_t retval = c3nodechild(*ctx.c3, index, 0);
	index = c3append(ctx.c3, C3_EXPR_RETURN, 2, ctx.current_fn, retval);
	if(index == -1){
		ERROR("OOM", 0);
		return 0;
	}
	ctx.tree->nodes[source] = index;
	ctx.c3->ast.nodes[source].node_index = index;
	// Note: this logic should be identical to the BREAK logic in analyze_node, most likely
	return u_node(index + 2, 1);
}


@@ 365,8 353,8 @@ u_analyze_node(uint32_t source)
{
	uint32_t index;
	c3tag tag;
	index = ctx.tree->nodes[source];
	tag = c3nodetag(*ctx.tree, index);
	index = ctx.c3->ast.nodes[source].node_index;
	tag = c3nodetag(*ctx.c3, index);
	if(tag == C3_EXPR_DEFER)
		return u_node(index + 1, 1);
	switch(tag){


@@ 453,22 441,22 @@ u_node(uint32_t source, int is_dep)
	uint32_t prev_scope, scope;
	c3tag tag;
	prev_scope = ctx.current_scope;
	index = ctx.tree->nodes[source];
	tag = c3nodetag(*ctx.tree, index);
	index = ctx.c3->ast.nodes[source].node_index;
	tag = c3nodetag(*ctx.c3, index);
	if(tag != C3_CONST && tag != C3_VAR){
		index = resolve_expr(index, prev_scope, &scope);
		if(index == -1){
			ERROR("Failed to resolve expr", 0);
			return 0;
		}
		ctx.tree->nodes[source] = index;
		ctx.c3->ast.nodes[source].node_index = index;
	}
	else
		scope = ctx.current_scope;
	dep_node = stbds_hmget(ctx.deps.analyzed, index);
	dep_node = stbds_hmget(ctx.c3->analysis.deps, index);
	if(dep_node != 0){
		if(dep_node == -1){
			ERROR("Recursive dep found on index %d (tag %s)", index, c3tagstr(c3nodetag(*ctx.tree, index)));
			ERROR("Recursive dep found on index %d (tag %s)", index, c3tagstr(c3nodetag(*ctx.c3, index)));
			return 0;
		}
		if(is_dep && !stb_sb_push_u32(&ctx.deps.children, dep_node)){


@@ 477,14 465,14 @@ u_node(uint32_t source, int is_dep)
		}
		return 1;
	}
	tag = c3nodetag(*ctx.tree, index);
	tag = c3nodetag(*ctx.c3, index);
	if(scope != -1 && tag != C3_CONST && tag != C3_FN_PARAM && !(scope_insert(scope, index) && scope_mark_used(scope, index)))
		return 0;
	parents_kids = ctx.deps.children;
	ctx.deps.children = NULL;
	if(!stb_sb_push_u32(&ctx.deps.children, 0))
	if(!stb_sb_push_u32(&ctx.deps.children, -1))
		OOM();
	stbds_hmput(ctx.deps.analyzed, index, -1);
	stbds_hmput(ctx.c3->analysis.deps, index, -1);
	ctx.current_scope = scope;
	if(!u_analyze_node(source)){
		ctx.deps.children = parents_kids;


@@ 493,18 481,16 @@ u_node(uint32_t source, int is_dep)
	}
	ctx.current_scope = prev_scope;
	// Add the patched index to the dep graph - might have been changed by scope resolution!
	index = ctx.tree->nodes[source];
	index = ctx.c3->ast.nodes[source].node_index;
	ctx.deps.children[0] = index;
	dep_node = c3appendarr(ctx.tree, C3_DEP, stb_sb_count(ctx.deps.children), ctx.deps.children);
	dep_node = c3appendarr(ctx.c3, C3_DEP, stb_sb_count(ctx.deps.children), ctx.deps.children);
	if(dep_node == -1)
		return 0;
	stb_sb_free(ctx.deps.children);
	ctx.deps.children = parents_kids;
	if(is_dep && !stb_sb_push_u32(&ctx.deps.children, dep_node))
		OOM();
	if(is_dep && !stb_sb_push_u32(&ctx.deps.top, dep_node))
		OOM();
	stbds_hmput(ctx.deps.analyzed, index, dep_node);
	stbds_hmput(ctx.c3->analysis.deps, index, dep_node);
	return 1;
}



@@ 512,7 498,7 @@ static int
u_block(uint32_t index)
{
	uint32_t i;
	uint16_t kids = c3nodekids(*ctx.tree, index);
	uint16_t kids = c3nodekids(*ctx.c3, index);
	if(!u_type(index + 1))
		return 0;
	for(i = 1; i < kids; i += 1){


@@ 529,22 515,22 @@ u_fnparams(uint32_t index)
{
	uint32_t i;
	uint32_t kid_index;
	c3tag tag = c3nodetag(*ctx.tree, index);
	uint16_t kids = c3nodekids(*ctx.tree, index);
	c3tag tag = c3nodetag(*ctx.c3, index);
	uint16_t kids = c3nodekids(*ctx.c3, index);
	if(tag != C3_FN_PARAM_LIST){
		ERROR("ICE: expected param list", 0);
		return 0;
	}
	for(i = 0; i < kids; i += 1){
		kid_index = c3nodechild(*ctx.tree, index, i);
		tag = c3nodetag(*ctx.tree, kid_index);
		kid_index = c3nodechild(*ctx.c3, index, i);
		tag = c3nodetag(*ctx.c3, kid_index);
		if(tag == C3_BAD){
			ERROR("ICE: getting parameter failed", 0);
			return 0;
		}
		if(tag == C3_COMPTIME){
			kid_index = c3nodechild(*ctx.tree, kid_index, 0);
			tag = c3nodetag(*ctx.tree, kid_index);
			kid_index = c3nodechild(*ctx.c3, kid_index, 0);
			tag = c3nodetag(*ctx.c3, kid_index);
			if(tag == C3_BAD){
				ERROR("ICE: getting comptime parameter failed", 0);
				return 0;


@@ 570,7 556,7 @@ u_func(uint32_t index)
	uint16_t conv;
	uint32_t fnrettype;
	uint32_t block;
	call_graph_node = u_find(&ctx.call_graph.top, index);
	call_graph_node = u_find(&ctx.c3->analysis.exports, index);
	if(call_graph_node != -1){
		i = u_find(&ctx.call_graph.children, index);
		if(i == -1)


@@ 589,70 575,59 @@ u_func(uint32_t index)
		return -1;
	}
	ctx.current_fn = index;
	i = c3nodechild(*ctx.tree, index, 0);
	conv = c3nodetag(*ctx.tree, c3nodechild(*ctx.tree, i, 2));;
	i = c3nodechild(*ctx.c3, index, 0);
	conv = c3nodetag(*ctx.c3, c3nodechild(*ctx.c3, i, 2));;
	if(!u_node(i + 1, 1)){
		ERROR("Failed to analyze function type", 0);
		return -1;
	}
	fnrettype = c3nodechild(*ctx.tree, i, 0);
	i = c3nodechild(*ctx.tree, i, 1);
	fnrettype = c3nodechild(*ctx.c3, i, 0);
	i = c3nodechild(*ctx.c3, i, 1);
	if(!u_fnparams(i)){
		ERROR("Unable to insert parameters into scope", 0);
		return -1;
	}
	block = c3nodechild(*ctx.tree, index, 1);
	ctx.tree->nodes[block + 1] = fnrettype;
	block = c3nodechild(*ctx.c3, index, 1);
	ctx.c3->ast.nodes[block + 1].node_index = fnrettype;
	if(!u_node(index + 2, 1)){
		ERROR("Failed to analyze block call graph", 0);
		call_graph_node = -1;
	}
	else{
		call_graph_node = c3appendarr(ctx.tree, C3_DEP, stb_sb_count(ctx.call_graph.children), ctx.call_graph.children);
		call_graph_node = c3appendarr(ctx.c3, C3_DEP, stb_sb_count(ctx.call_graph.children), ctx.call_graph.children);
	}
	ctx.current_scope = prev_scope;
	ctx.current_fn = prev_fn;
	stb_sb_free(ctx.call_graph.children);
	ctx.call_graph.children = parent_list;
	if(conv != C3_CALL_CONV_INLINE)
		if(!stb_sb_push_u32(&ctx.call_graph.top, call_graph_node))
		if(!stb_sb_push_u32(&ctx.c3->analysis.fns, call_graph_node))
			OOM();
	return call_graph_node;
}

static void
ctx_init(struct c3tree *tree)
ctx_init(c3ctx *c3ctx)
{
	ctx.call_graph.top = NULL;
	ctx.call_graph.children = NULL;
	ctx.deps.analyzed = NULL;
	ctx.deps.top = NULL;
	ctx.deps.children = NULL;
	ctx.current_fn = -1;
	ctx.current_scope = -1;
	ctx.deepest_container = -1;
	ctx.tests = NULL;
	ctx.tree = tree;
	ctx.c3 = c3ctx;
}

// This is the entry point for tree resolution.
int
resolve_tree(struct c3tree *tree)
resolve_tree(c3ctx *c3ctx)
{
	ctx_init(tree);
	scope_init(tree);
	ctx_init(c3ctx);
	scope_init(c3ctx);
	if(!u_node(1, 1)){
		ERROR("tree resolution failed", 0);
		ERROR("ctx resolution failed", 0);
		return 0;
	}
	if(!stb_sb_push_u32(&ctx.call_graph.top, -1))
		OOM();
	tree->graphs.call = c3appendarr(tree, C3_DEP, stb_sb_count(ctx.call_graph.top), ctx.call_graph.top);
	tree->graphs.deps = c3appendarr(tree, C3_DEP, stb_sb_count(ctx.deps.top), ctx.deps.top);
	tree->graphs.tests = c3appendarr(tree, C3_DEP, stb_sb_count(ctx.tests), ctx.tests);
	stb_sb_free(ctx.tests);
	stb_sb_free(ctx.call_graph.top);
	stb_sb_free(ctx.deps.children);
	stb_sb_free(ctx.deps.top);
	return 1;
}

D tree.h => tree.h +0 -39
@@ 1,39 0,0 @@
struct c3string{
	uint32_t index;
	uint32_t length;
};

// Arrays are stored with a hidden length and capacity field which gets serialized by the read/write functions
struct c3tree{
	uint32_t version;
	uint32_t *nodes;
	struct c3string *strings;
	char *chars;
// NOT part of the protocol! These are implementation-internal.
	struct {
		uint32_t call, deps, type, tests;
	}graphs;
};

// All functions which take in an index require bounds checking to be handled by
// the caller.
void c3init(struct c3tree *);
void c3free(struct c3tree *);
int c3read(struct c3tree*, int fd);
int c3write(struct c3tree*, int fd);

// If there is not sufficient space, this attempts to reserve space at a rate expanding exponentially
// This returns the new index, or -1 on failure
uint32_t c3append(struct c3tree *, c3tag tag, uint16_t kid_count, ...);
uint32_t c3appendarr(struct c3tree *, c3tag tag, uint16_t kid_count, uint32_t *kids);
uint32_t c3intern(struct c3tree *, char *, uint32_t len);
char *h_c3str(struct c3tree, uint32_t string_handle);

void c3nodedump(struct c3tree tree, uint32_t index, uint32_t indent, int recurse);
void c3dump(struct c3tree tree);

c3tag c3nodetag(struct c3tree, uint32_t index);
uint16_t c3nodekids(struct c3tree, uint32_t index);
uint32_t c3nodechild(struct c3tree, uint32_t index, uint32_t child);
void c3nodeset(struct c3tree, uint32_t index, c3tag tag, uint16_t kid_count);