[PATCH] sparse LLVM backend (was Re: [rfc] built-in native compilerfor Linux?)

From: Jeff Garzik
Date: Fri Apr 24 2009 - 22:07:19 EST



(resent -- the mail monster apparently ate the first one)

Just to make this crazy thread even crazier, I have successfully gotten sparse to create a valid x86-64 object file from C source code (...which I then executed).

The attached patch adds a code generator backend to sparse, that generates LLVM bitcode. This bitcode can then be converted to ELF .o files using standard tools -- see instructions in the header of s2l.c.

Do I expect this to be used in the kernel? Not really. I'm just doing this for fun, to add a compiler backend to sparse. It was pretty easy to get integer and logical operations going.

The work is based on compile-i386; as it turns out, LLVM's bitcode can be made type-aware, so I wanted to see how things would turn out generating code at a higher level. Given the higher-level constructs, I didn't feel the need to bother with linearized form.

Caveats...

- don't stray too far from simple, integer types
- bitfields caused explosions, but you knew that anyway

Cheers all,

Jeff


.gitignore | 2
Makefile | 3
s2l-gen.c | 2101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
s2l.c | 77 ++
4 files changed, 2182 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index a915ef3..eee93ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*.o
*.a
*.so
+*.o.d

# generated
pre-process.h
@@ -20,6 +21,7 @@ example
test-unssa
ctags
c2xml
+s2l

# tags
tags
diff --git a/Makefile b/Makefile
index 15daba5..f993cf8 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ INCLUDEDIR=$(PREFIX)/include
PKGCONFIGDIR=$(LIBDIR)/pkgconfig

PROGRAMS=test-lexing test-parsing obfuscate compile graph sparse \
- test-linearize example test-unssa test-dissect ctags
+ test-linearize example test-unssa test-dissect ctags s2l
INST_PROGRAMS=sparse cgcc
INST_MAN1=sparse.1 cgcc.1

@@ -108,6 +108,7 @@ sparse.pc: sparse.pc.in


compile_EXTRA_DEPS = compile-i386.o
+s2l_EXTRA_DEPS = s2l-gen.o

PROG_LINK_CMD = $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ $($@_EXTRA_OBJS)

diff --git a/s2l-gen.c b/s2l-gen.c
new file mode 100644
index 0000000..029d56b
--- /dev/null
+++ b/s2l-gen.c
@@ -0,0 +1,2101 @@
+
+/*
+ * sparse/s2l-gen.c
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ * 2003 Linus Torvalds
+ * Copyright 2003 Jeff Garzik
+ * Copyright 2009 Red Hat, Inc.
+ *
+ * Licensed under the Open Software License version 1.1
+ *
+ *
+ * A cheesy LLVM backend for sparse. Outputs LLVM ASCII bitcode,
+ * given a C source code input.
+ *
+ *
+ * TODO list:
+ * 1) fill in TODO list
+ * 2) ?
+ * 3) Profit!
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "scope.h"
+#include "expression.h"
+#include "target.h"
+#include "compile.h"
+#include "bitmap.h"
+
+struct textbuf {
+ unsigned int len; /* does NOT include terminating null */
+ char *text;
+ struct textbuf *next;
+ struct textbuf *prev;
+};
+
+struct loop_stack {
+ int continue_lbl;
+ int loop_bottom_lbl;
+ struct loop_stack *next;
+};
+
+struct atom;
+struct storage;
+DECLARE_PTR_LIST(str_list, struct atom);
+DECLARE_PTR_LIST(atom_list, struct atom);
+DECLARE_PTR_LIST(storage_list, struct storage);
+
+struct function {
+ int stack_size;
+ int pseudo_nr;
+ struct storage_list *pseudo_list;
+ struct atom_list *atom_list;
+ struct str_list *str_list;
+ struct loop_stack *loop_stack;
+ struct symbol **argv;
+ unsigned int argc;
+ int ret_target;
+};
+
+enum storage_type {
+ STOR_PSEUDO, /* variable stored on the stack */
+ STOR_ARG, /* function argument */
+ STOR_SYM, /* a symbol we can directly ref in the asm */
+ STOR_VALUE, /* integer constant */
+ STOR_LABEL, /* label / jump target */
+ STOR_LABELSYM, /* label generated from symbol's pointer value */
+};
+
+struct storage {
+ enum storage_type type;
+ unsigned long flags;
+
+ /* STOR_REG */
+ struct symbol *ctype;
+
+ union {
+ /* STOR_PSEUDO */
+ struct {
+ int pseudo;
+ int offset;
+ int size;
+ };
+ /* STOR_ARG */
+ struct {
+ int idx;
+ };
+ /* STOR_SYM */
+ struct {
+ struct symbol *sym;
+ };
+ /* STOR_VALUE */
+ struct {
+ long long value;
+ };
+ /* STOR_LABEL */
+ struct {
+ int label;
+ };
+ /* STOR_LABELSYM */
+ struct {
+ struct symbol *labelsym;
+ };
+ };
+};
+
+enum {
+ STOR_LABEL_VAL = (1 << 0),
+ STOR_WANTS_FREE = (1 << 1),
+};
+
+struct symbol_private {
+ struct storage *addr;
+};
+
+enum atom_type {
+ ATOM_TEXT,
+ ATOM_CSTR,
+};
+
+struct atom {
+ enum atom_type type;
+ union {
+ /* stuff for text */
+ struct {
+ char *text;
+ unsigned int text_len; /* w/o terminating null */
+ };
+
+ /* stuff for C strings */
+ struct {
+ struct string *string;
+ int label;
+ };
+ };
+};
+
+
+static struct function *current_func = NULL;
+static struct textbuf *unit_post_text = NULL;
+static const char *current_section;
+
+static void emit_comment(const char * fmt, ...) FORMAT_ATTR(1);
+static void emit_move(struct storage *src, struct storage *dest,
+ struct symbol *ctype, const char *comment);
+static int type_is_signed(struct symbol *sym);
+static struct storage *s2l_gen_address_gen(struct expression *expr);
+static struct storage *s2l_gen_symbol_expr(struct symbol *sym);
+static void s2l_gen_symbol(struct symbol *sym);
+static struct storage *s2l_gen_statement(struct statement *stmt);
+static struct storage *s2l_gen_expression(struct expression *expr);
+
+static void stor_sym_init(struct symbol *sym)
+{
+ struct storage *stor;
+ struct symbol_private *priv;
+
+ priv = calloc(1, sizeof(*priv) + sizeof(*stor));
+ if (!priv)
+ die("OOM in stor_sym_init");
+
+ stor = (struct storage *) (priv + 1);
+
+ priv->addr = stor;
+ stor->type = STOR_SYM;
+ stor->sym = sym;
+}
+
+static const char *stor_op_name(struct storage *s)
+{
+ static char name[32];
+
+ switch (s->type) {
+ case STOR_PSEUDO:
+ sprintf(name, "%%tmp%u", s->pseudo);
+ break;
+ case STOR_ARG:
+ sprintf(name, "%%arg%u", s->idx);
+ break;
+ case STOR_SYM:
+ strcpy(name, show_ident(s->sym->ident));
+ break;
+ case STOR_VALUE:
+ sprintf(name, "%Ld", s->value);
+ break;
+ case STOR_LABEL:
+ sprintf(name, "@L%d", s->label);
+ break;
+ case STOR_LABELSYM:
+ sprintf(name, "%%LS%p", s->labelsym);
+ break;
+ }
+
+ return name;
+}
+
+static struct atom *new_atom(enum atom_type type)
+{
+ struct atom *atom;
+
+ atom = calloc(1, sizeof(*atom)); /* TODO: chunked alloc */
+ if (!atom)
+ die("nuclear OOM");
+
+ atom->type = type;
+
+ return atom;
+}
+
+static inline void push_cstring(struct function *f, struct string *str,
+ int label)
+{
+ struct atom *atom;
+
+ atom = new_atom(ATOM_CSTR);
+ atom->string = str;
+ atom->label = label;
+
+ add_ptr_list(&f->str_list, atom); /* note: _not_ atom_list */
+}
+
+static inline void push_atom(struct function *f, struct atom *atom)
+{
+ add_ptr_list(&f->atom_list, atom);
+}
+
+static void push_text_atom(struct function *f, const char *text)
+{
+ struct atom *atom = new_atom(ATOM_TEXT);
+
+ atom->text = strdup(text);
+ atom->text_len = strlen(text);
+
+ push_atom(f, atom);
+}
+
+static struct storage *new_storage(enum storage_type type)
+{
+ struct storage *stor;
+
+ stor = calloc(1, sizeof(*stor));
+ if (!stor)
+ die("OOM in new_storage");
+
+ stor->type = type;
+
+ return stor;
+}
+
+static struct storage *stack_alloc(int n_bytes)
+{
+ struct function *f = current_func;
+ struct storage *stor;
+
+ assert(f != NULL);
+
+ stor = new_storage(STOR_PSEUDO);
+ stor->type = STOR_PSEUDO;
+ stor->pseudo = f->pseudo_nr;
+ stor->offset = f->stack_size; /* FIXME: stack req. natural align */
+ stor->size = n_bytes;
+ f->stack_size += n_bytes;
+ f->pseudo_nr++;
+
+ add_ptr_list(&f->pseudo_list, stor);
+
+ return stor;
+}
+
+static struct storage *new_labelsym(struct symbol *sym)
+{
+ struct storage *stor;
+
+ stor = new_storage(STOR_LABELSYM);
+
+ if (stor) {
+ stor->flags |= STOR_WANTS_FREE;
+ stor->labelsym = sym;
+ }
+
+ return stor;
+}
+
+static int new_label(void)
+{
+ static int label = 0;
+ return ++label;
+}
+
+static void textbuf_push(struct textbuf **buf_p, const char *text)
+{
+ struct textbuf *tmp, *list = *buf_p;
+ unsigned int text_len = strlen(text);
+ unsigned int alloc_len = text_len + 1 + sizeof(*list);
+
+ tmp = calloc(1, alloc_len);
+ if (!tmp)
+ die("OOM on textbuf alloc");
+
+ tmp->text = ((void *) tmp) + sizeof(*tmp);
+ memcpy(tmp->text, text, text_len + 1);
+ tmp->len = text_len;
+
+ /* add to end of list */
+ if (!list) {
+ list = tmp;
+ tmp->prev = tmp;
+ } else {
+ tmp->prev = list->prev;
+ tmp->prev->next = tmp;
+ list->prev = tmp;
+ }
+ tmp->next = list;
+
+ *buf_p = list;
+}
+
+#if 0
+static void textbuf_emit(struct textbuf **buf_p)
+{
+ struct textbuf *tmp, *list = *buf_p;
+
+ while (list) {
+ tmp = list;
+ if (tmp->next == tmp)
+ list = NULL;
+ else {
+ tmp->prev->next = tmp->next;
+ tmp->next->prev = tmp->prev;
+ list = tmp->next;
+ }
+
+ fputs(tmp->text, stdout);
+
+ free(tmp);
+ }
+
+ *buf_p = list;
+}
+#endif
+
+static void emit_comment(const char *fmt, ...)
+{
+ struct function *f = current_func;
+ static char tmpbuf[100] = "\t\t\t\t\t; ";
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(tmpbuf+7, sizeof(tmpbuf)-4, fmt, args);
+ va_end(args);
+ tmpbuf[i+7] = '\n';
+ tmpbuf[i+8] = '\0';
+ push_text_atom(f, tmpbuf);
+}
+
+static void emit_label (int label, const char *comment)
+{
+ struct function *f = current_func;
+ char s[64];
+
+ if (!comment)
+ sprintf(s, ".L%d:\n", label);
+ else
+ sprintf(s, ".L%d:\t\t\t\t\t; %s\n", label, comment);
+
+ push_text_atom(f, s);
+}
+
+static void emit_labelsym (struct symbol *sym, const char *comment)
+{
+ struct function *f = current_func;
+ char s[64];
+
+ if (!comment)
+ sprintf(s, ".LS%p:\n", sym);
+ else
+ sprintf(s, ".LS%p:\t\t\t\t; %s\n", sym, comment);
+
+ push_text_atom(f, s);
+}
+
+void emit_unit_begin(const char *basename)
+{
+}
+
+void emit_unit_end(void)
+{
+}
+
+/* conditionally switch sections */
+static void emit_section(const char *s)
+{
+ if (s == current_section)
+ return;
+ if (current_section && (!strcmp(s, current_section)))
+ return;
+
+#if 0
+ printf("\t%s\n", s);
+#endif
+ current_section = s;
+}
+
+static void emit_atom_list(struct function *f)
+{
+ struct atom *atom;
+
+ FOR_EACH_PTR(f->atom_list, atom) {
+ switch (atom->type) {
+ case ATOM_TEXT: {
+ ssize_t rc = write(STDOUT_FILENO, atom->text,
+ atom->text_len);
+ (void) rc; /* FIXME */
+ break;
+ }
+ case ATOM_CSTR:
+ assert(0);
+ break;
+ }
+ } END_FOR_EACH_PTR(atom);
+}
+
+static void emit_string_list(struct function *f)
+{
+ struct atom *atom;
+ size_t len;
+
+ emit_section(".section\t.rodata");
+
+ FOR_EACH_PTR(f->str_list, atom) {
+ /* FIXME: escape " in string */
+ /* FIXME 2: add trailing nul!!! */
+ len = strlen(show_string(atom->string)),
+ printf("@.L%d = internal constant [%lu x i8] c%s\n",
+ atom->label,
+ (unsigned long) len,
+ show_string(atom->string));
+
+ free(atom);
+ } END_FOR_EACH_PTR(atom);
+
+ printf("\n");
+}
+
+static void func_cleanup(struct function *f)
+{
+ struct storage *stor;
+ struct atom *atom;
+
+ FOR_EACH_PTR(f->pseudo_list, stor) {
+ free(stor);
+ } END_FOR_EACH_PTR(stor);
+
+ FOR_EACH_PTR(f->atom_list, atom) {
+ if ((atom->type == ATOM_TEXT) && (atom->text))
+ free(atom->text);
+ free(atom);
+ } END_FOR_EACH_PTR(atom);
+
+ free_ptr_list(&f->pseudo_list);
+ free(f);
+}
+
+static const char *s2l_show_type(struct symbol *base_type)
+{
+ static char buf[256];
+
+ if (base_type == &void_ctype)
+ return "void";
+
+ sprintf(buf, "i%d", base_type->bit_size);
+ return buf;
+}
+
+/* function prologue */
+static void emit_func_pre(struct symbol *sym)
+{
+ struct function *f;
+ struct symbol *arg, *arg_bt;
+ unsigned int i, argc = 0, alloc_len;
+ unsigned char *mem;
+ struct symbol_private *privbase;
+ struct storage *storage_base;
+ struct symbol *base_type = sym->ctype.base_type;
+ struct symbol *ret_type = sym->ctype.base_type->ctype.base_type;
+ int first_arg = 1;
+ char defstr[512], stmp[128];
+
+ sprintf(defstr, "define %s%s @%s (",
+ (sym->ctype.modifiers & MOD_STATIC) ? "internal " : "",
+ s2l_show_type(ret_type),
+ show_ident(sym->ident));
+
+ FOR_EACH_PTR(base_type->arguments, arg) {
+ const char *s;
+
+ arg_bt = arg->ctype.base_type;
+
+ s = s2l_show_type(arg_bt);
+
+ sprintf(stmp, "%s%s %%arg%u",
+ first_arg ? "" : ", ",
+ s, argc++);
+
+ strcat(defstr, stmp);
+
+ first_arg = 0;
+ } END_FOR_EACH_PTR(arg);
+
+ strcat(defstr, ") nounwind {\nentry:\n");
+
+ alloc_len =
+ sizeof(*f) +
+ (argc * sizeof(struct symbol *)) +
+ (argc * sizeof(struct symbol_private)) +
+ (argc * sizeof(struct storage));
+ mem = calloc(1, alloc_len);
+ if (!mem)
+ die("OOM on func info");
+
+ f = (struct function *) mem;
+ mem += sizeof(*f);
+ f->argv = (struct symbol **) mem;
+ mem += (argc * sizeof(struct symbol *));
+ privbase = (struct symbol_private *) mem;
+ mem += (argc * sizeof(struct symbol_private));
+ storage_base = (struct storage *) mem;
+
+ f->argc = argc;
+ f->ret_target = new_label();
+
+ i = 0;
+ FOR_EACH_PTR(base_type->arguments, arg) {
+ f->argv[i] = arg;
+ arg->aux = &privbase[i];
+ storage_base[i].type = STOR_ARG;
+ storage_base[i].idx = i;
+ storage_base[i].size = arg->ctype.base_type->bit_size / 8;
+ privbase[i].addr = &storage_base[i];
+ i++;
+ } END_FOR_EACH_PTR(arg);
+
+ assert(current_func == NULL);
+ current_func = f;
+
+ push_text_atom(current_func, defstr);
+
+}
+
+/* function epilogue */
+static void emit_func_post(struct symbol *sym)
+{
+ struct function *f = current_func;
+
+ if (f->str_list)
+ emit_string_list(f);
+
+ /* function epilogue */
+
+ /* output everything to stdout */
+ fflush(stdout); /* paranoia; needed? */
+ emit_atom_list(f);
+
+ /* function footer */
+ printf("}\n\n");
+
+ /* FIXME: issue 'ret' if not already done */
+
+ func_cleanup(f);
+ current_func = NULL;
+}
+
+/* emit object (a.k.a. variable, a.k.a. data) prologue */
+static void emit_object_pre(const char *name, unsigned long modifiers,
+ unsigned long alignment, unsigned int byte_size)
+{
+ if ((modifiers & MOD_STATIC) == 0)
+ printf(".globl %s\n", name);
+ emit_section(".data");
+ if (alignment)
+ printf("\t.align %lu\n", alignment);
+ printf("\t.type\t%s, @object\n", name);
+ printf("\t.size\t%s, %d\n", name, byte_size);
+ printf("%s:\n", name);
+}
+
+/* emit value (only) for an initializer scalar */
+static void emit_scalar(struct expression *expr, unsigned int bit_size)
+{
+ const char *type;
+ long long ll;
+
+ assert(expr->type == EXPR_VALUE);
+
+ if (expr->value == 0ULL) {
+ printf("\t.zero\t%d\n", bit_size / 8);
+ return;
+ }
+
+ ll = (long long) expr->value;
+
+ switch (bit_size) {
+ case 8: type = "byte"; ll = (char) ll; break;
+ case 16: type = "value"; ll = (short) ll; break;
+ case 32: type = "long"; ll = (int) ll; break;
+ case 64: type = "quad"; break;
+ default: type = NULL; break;
+ }
+
+ assert(type != NULL);
+
+ printf("\t.%s\t%Ld\n", type, ll);
+}
+
+static void emit_global_noinit(const char *name, unsigned long modifiers,
+ unsigned long alignment, unsigned int byte_size)
+{
+ char s[64];
+
+ if (modifiers & MOD_STATIC) {
+ sprintf(s, "\t.local\t%s\n", name);
+ textbuf_push(&unit_post_text, s);
+ }
+ if (alignment)
+ sprintf(s, "\t.comm\t%s,%d,%lu\n", name, byte_size, alignment);
+ else
+ sprintf(s, "\t.comm\t%s,%d\n", name, byte_size);
+ textbuf_push(&unit_post_text, s);
+}
+
+static int ea_current, ea_last;
+
+static void emit_initializer(struct symbol *sym,
+ struct expression *expr)
+{
+ int distance = ea_current - ea_last - 1;
+
+ if (distance > 0)
+ printf("\t.zero\t%d\n", (sym->bit_size / 8) * distance);
+
+ if (expr->type == EXPR_VALUE) {
+ struct symbol *base_type = sym->ctype.base_type;
+ assert(base_type != NULL);
+
+ emit_scalar(expr, sym->bit_size / get_expression_value(base_type->array_size));
+ return;
+ }
+ if (expr->type != EXPR_INITIALIZER)
+ return;
+
+ assert(0); /* FIXME */
+}
+
+static int sort_array_cmp(const struct expression *a,
+ const struct expression *b)
+{
+ int a_ofs = 0, b_ofs = 0;
+
+ if (a->type == EXPR_POS)
+ a_ofs = (int) a->init_offset;
+ if (b->type == EXPR_POS)
+ b_ofs = (int) b->init_offset;
+
+ return a_ofs - b_ofs;
+}
+
+/* move to front-end? */
+static void sort_array(struct expression *expr)
+{
+ struct expression *entry, **list;
+ unsigned int elem, sorted, i;
+
+ elem = 0;
+ FOR_EACH_PTR(expr->expr_list, entry) {
+ elem++;
+ } END_FOR_EACH_PTR(entry);
+
+ if (!elem)
+ return;
+
+ list = malloc(sizeof(entry) * elem);
+ if (!list)
+ die("OOM in sort_array");
+
+ /* this code is no doubt evil and ignores EXPR_INDEX possibly
+ * to its detriment and other nasty things. improvements
+ * welcome.
+ */
+ i = 0;
+ sorted = 0;
+ FOR_EACH_PTR(expr->expr_list, entry) {
+ if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE)) {
+ /* add entry to list[], in sorted order */
+ if (sorted == 0) {
+ list[0] = entry;
+ sorted = 1;
+ } else {
+ for (i = 0; i < sorted; i++)
+ if (sort_array_cmp(entry, list[i]) <= 0)
+ break;
+
+ /* If inserting into the middle of list[]
+ * instead of appending, we memmove.
+ * This is ugly, but thankfully
+ * uncommon. Input data with tons of
+ * entries very rarely have explicit
+ * offsets. convert to qsort eventually...
+ */
+ if (i != sorted)
+ memmove(&list[i + 1], &list[i],
+ (sorted - i) * sizeof(entry));
+ list[i] = entry;
+ sorted++;
+ }
+ }
+ } END_FOR_EACH_PTR(entry);
+
+ i = 0;
+ FOR_EACH_PTR(expr->expr_list, entry) {
+ if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE))
+ *THIS_ADDRESS(entry) = list[i++];
+ } END_FOR_EACH_PTR(entry);
+
+}
+
+static void emit_array(struct symbol *sym)
+{
+ struct symbol *base_type = sym->ctype.base_type;
+ struct expression *expr = sym->initializer;
+ struct expression *entry;
+
+ assert(base_type != NULL);
+
+ stor_sym_init(sym);
+
+ ea_last = -1;
+
+ emit_object_pre(show_ident(sym->ident), sym->ctype.modifiers,
+ sym->ctype.alignment,
+ sym->bit_size / 8);
+
+ sort_array(expr);
+
+ FOR_EACH_PTR(expr->expr_list, entry) {
+ if (entry->type == EXPR_VALUE) {
+ ea_current = 0;
+ emit_initializer(sym, entry);
+ ea_last = ea_current;
+ } else if (entry->type == EXPR_POS) {
+ ea_current =
+ entry->init_offset / (base_type->bit_size / 8);
+ emit_initializer(sym, entry->init_expr);
+ ea_last = ea_current;
+ }
+ } END_FOR_EACH_PTR(entry);
+}
+
+void emit_one_symbol(struct symbol *sym)
+{
+ s2l_gen_symbol(sym);
+}
+
+static void emit_copy(struct storage *src, struct storage *dest,
+ struct symbol *ctype)
+{
+ struct storage *tmp;
+ unsigned int bit_size;
+
+ /* FIXME: Bitfield copy! */
+
+ bit_size = src->size * 8;
+ if (!bit_size)
+ bit_size = 32;
+ if ((src->type == STOR_ARG) && (bit_size < 32))
+ bit_size = 32;
+
+ tmp = stack_alloc(bit_size / 8);
+
+ emit_move(src, tmp, ctype, "begin copy ..");
+
+ bit_size = dest->size * 8;
+ if (!bit_size)
+ bit_size = 32;
+ if ((dest->type == STOR_ARG) && (bit_size < 32))
+ bit_size = 32;
+
+ emit_move(tmp, dest, ctype, ".... end copy");
+}
+
+static void emit_store(struct expression *dest_expr, struct storage *dest,
+ struct storage *src, int bits)
+{
+ /* FIXME: Bitfield store! */
+ printf("\tst.%d\t\tv%d,[v%d]\n", bits, src->pseudo, dest->pseudo);
+}
+
+static void emit_scalar_noinit(struct symbol *sym)
+{
+ emit_global_noinit(show_ident(sym->ident),
+ sym->ctype.modifiers, sym->ctype.alignment,
+ sym->bit_size / 8);
+ stor_sym_init(sym);
+}
+
+static void emit_array_noinit(struct symbol *sym)
+{
+ emit_global_noinit(show_ident(sym->ident),
+ sym->ctype.modifiers, sym->ctype.alignment,
+ get_expression_value(sym->array_size) * (sym->bit_size / 8));
+ stor_sym_init(sym);
+}
+
+static void emit_move(struct storage *src, struct storage *dest,
+ struct symbol *ctype, const char *comment)
+{
+ unsigned int bits;
+ unsigned int is_signed;
+ char insnstr[128];
+ char stor_src[16], stor_dest[16];
+
+ if (ctype) {
+ bits = ctype->bit_size;
+ is_signed = type_is_signed(ctype);
+ } else {
+ bits = 32;
+ is_signed = 0;
+ }
+
+ strcpy(stor_src, stor_op_name(src));
+ strcpy(stor_dest, stor_op_name(dest));
+
+ sprintf(insnstr, "\t%s = add i%d 0, %s\t; %s\n",
+ stor_dest,
+ bits,
+ stor_src,
+ comment);
+ push_text_atom(current_func, insnstr);
+}
+
+static struct storage *emit_compare(struct expression *expr)
+{
+ struct function *f = current_func;
+ struct storage *left = s2l_gen_expression(expr->left);
+ struct storage *right = s2l_gen_expression(expr->right);
+ struct storage *val;
+ const char *opname = NULL;
+ unsigned int right_bits = expr->right->ctype->bit_size;
+ char insnstr[128];
+ char stor_val[16], stor_left[16], stor_right[16];
+
+ switch(expr->op) {
+ case '<':
+ opname = "slt";
+ break;
+ case SPECIAL_UNSIGNED_LT:
+ opname = "ult";
+ break;
+ case '>':
+ opname = "sgt";
+ break;
+ case SPECIAL_UNSIGNED_GT:
+ opname = "ugt";
+ break;
+ case SPECIAL_LTE:
+ opname = "sle";
+ break;
+ case SPECIAL_UNSIGNED_LTE:
+ opname = "ule";
+ break;
+ case SPECIAL_GTE:
+ opname = "sge";
+ break;
+ case SPECIAL_UNSIGNED_GTE:
+ opname = "uge";
+ break;
+ case SPECIAL_EQUAL:
+ opname = "eq";
+ break;
+ case SPECIAL_NOTEQUAL:
+ opname = "ne";
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ /* init to 0 */
+ val = stack_alloc(right_bits / 8);
+ val->flags = STOR_WANTS_FREE;
+
+ strcpy(stor_val, stor_op_name(val));
+ strcpy(stor_left, stor_op_name(left));
+ strcpy(stor_right, stor_op_name(right));
+
+ sprintf(insnstr, "\t%s = icmp %s i%d %s, %s\n",
+ stor_val,
+ opname,
+ right_bits,
+ stor_left,
+ stor_right);
+ push_text_atom(f, insnstr);
+
+ return val;
+}
+
+static struct storage *emit_value(struct expression *expr)
+{
+ struct storage *val;
+
+ val = new_storage(STOR_VALUE);
+ val->value = (long long) expr->value;
+
+ return val; /* FIXME: memory leak */
+}
+
+static struct storage *emit_binop(struct expression *expr)
+{
+ struct function *f = current_func;
+ struct storage *left = s2l_gen_expression(expr->left);
+ struct storage *right = s2l_gen_expression(expr->right);
+ struct storage *new;
+ const char *opname = NULL;
+ char insnstr[128];
+ char stor_new[16], stor_left[16], stor_right[16];
+ int is_signed;
+
+ is_signed = type_is_signed(expr->ctype);
+
+ switch (expr->op) {
+ case '+':
+ opname = "add";
+ break;
+ case '-':
+ opname = "sub";
+ break;
+ case '&':
+ opname = "and";
+ break;
+ case '|':
+ opname = "or";
+ break;
+ case '^':
+ opname = "xor";
+ break;
+ case SPECIAL_LEFTSHIFT:
+ opname = "shl";
+ break;
+ case SPECIAL_RIGHTSHIFT:
+ if (is_signed)
+ opname = "lshr";
+ else
+ opname = "ashr";
+ break;
+ case '*':
+ opname = "mul";
+ break;
+ case '/':
+ if (is_signed)
+ opname = "sdiv";
+ else
+ opname = "udiv";
+ break;
+ case '%':
+ if (is_signed)
+ opname = "srem";
+ else
+ opname = "urem";
+ break;
+ default:
+ error_die(expr->pos, "unhandled binop '%s'\n", show_special(expr->op));
+ break;
+ }
+
+ new = stack_alloc(expr->ctype->bit_size / 8);
+
+ strcpy(stor_new, stor_op_name(new));
+ strcpy(stor_left, stor_op_name(left));
+ strcpy(stor_right, stor_op_name(right));
+
+ sprintf(insnstr, "\t%s = %s i%d %s, %s\n",
+ stor_new,
+ opname,
+ expr->ctype->bit_size,
+ stor_left,
+ stor_right);
+ push_text_atom(f, insnstr);
+
+ return new;
+}
+
+static void emit_conditional_test(struct storage *val,
+ struct storage **t_out,
+ struct storage **f_out)
+{
+ struct storage *tmp, *lbl_true, *lbl_false;
+ int target_true, target_false;
+ char insnstr[128];
+ char stor_val[16], stor_tmp[16];
+ char stor_true[16], stor_false[16];
+
+ emit_comment("begin if/conditional");
+
+ tmp = stack_alloc(val->size);
+
+ target_true = new_label();
+ lbl_true = new_storage(STOR_LABEL);
+ lbl_true->label = target_true;
+ lbl_true->flags = STOR_WANTS_FREE;
+
+ *t_out = lbl_true;
+
+ target_false = new_label();
+ lbl_false = new_storage(STOR_LABEL);
+ lbl_false->label = target_false;
+ lbl_false->flags = STOR_WANTS_FREE;
+
+ *f_out = lbl_false;
+
+ strcpy(stor_val, stor_op_name(val));
+ strcpy(stor_tmp, stor_op_name(tmp));
+ strcpy(stor_true, stor_op_name(lbl_true));
+ strcpy(stor_false, stor_op_name(lbl_false));
+
+ sprintf(insnstr, "\t%s = icmp eq i%d 0, %s\n",
+ stor_tmp,
+ val->size * 8,
+ stor_val);
+ push_text_atom(current_func, insnstr);
+
+ sprintf(insnstr, "\tbr i1 %s, label %s, label %s\n",
+ stor_tmp,
+ stor_true,
+ stor_false);
+ push_text_atom(current_func, insnstr);
+}
+
+static void emit_conditional_end(struct storage *l_false,
+ struct storage *cond_end_st)
+{
+ char insnstr[128];
+
+ sprintf(insnstr, "\tbr label %s\n",
+ stor_op_name(cond_end_st));
+ push_text_atom(current_func, insnstr);
+
+ emit_label(l_false->label, "if false");
+}
+
+static void emit_if_conditional(struct statement *stmt)
+{
+ struct storage *val;
+ struct storage *l_true = NULL, *l_false = NULL;
+ struct storage *cond_end_st;
+ int cond_end;
+
+ cond_end = new_label();
+ cond_end_st = new_storage(STOR_LABEL);
+ cond_end_st->label = cond_end;
+ cond_end_st->flags = STOR_WANTS_FREE;
+
+ /* emit test portion of conditional */
+ val = s2l_gen_expression(stmt->if_conditional);
+ emit_conditional_test(val, &l_true, &l_false);
+
+ emit_label(l_true->label, "if true");
+
+ /* emit if-true statement */
+ s2l_gen_statement(stmt->if_true);
+
+ emit_conditional_end(l_false, cond_end_st);
+
+ /* emit if-false statement, if present */
+ s2l_gen_statement(stmt->if_false);
+
+ /* end of conditional; jump target for if-true branch */
+ emit_label(cond_end, "end if");
+}
+
+static struct storage *emit_inc_dec(struct expression *expr, int postop)
+{
+ struct storage *addr = s2l_gen_address_gen(expr->unop);
+ struct storage *retval;
+ char opname[16];
+ char insnstr[128];
+ char stor_addr[16], stor_retval[16];
+
+ strcpy(opname, expr->op == SPECIAL_INCREMENT ? "add" : "sub");
+
+ if (postop) {
+ struct storage *new = stack_alloc(4);
+
+ emit_copy(addr, new, expr->unop->ctype);
+
+ retval = new;
+ } else
+ retval = addr;
+
+ strcpy(stor_addr, stor_op_name(addr));
+ strcpy(stor_retval, stor_op_name(retval));
+
+ sprintf(insnstr, "\t%s = %s i%d %s, 1\n",
+ stor_retval,
+ opname,
+ expr->ctype->bit_size,
+ stor_addr);
+ push_text_atom(current_func, insnstr);
+
+ return retval;
+}
+
+static struct storage *emit_postop(struct expression *expr)
+{
+ return emit_inc_dec(expr, 1);
+}
+
+static struct storage *emit_return_stmt(struct statement *stmt)
+{
+ struct function *f = current_func;
+ struct expression *expr = stmt->ret_value;
+ struct storage *val = NULL;
+
+ if (expr && expr->ctype) {
+ char s[64];
+
+ val = s2l_gen_expression(expr);
+ assert(val != NULL);
+
+ sprintf(s, "\tret i%d %s\n",
+ val->size * 8,
+ stor_op_name(val));
+ push_text_atom(f, s);
+ } else
+ push_text_atom(f, "\tret void\n");
+
+ return val;
+}
+
+static struct storage *emit_conditional_expr(struct expression *expr)
+{
+ struct storage *cond, *true = NULL, *false = NULL;
+ struct storage *new = stack_alloc(expr->ctype->bit_size / 8);
+ struct storage *l_true = NULL, *l_false = NULL;
+ struct storage *cond_end_st;
+ int cond_end;
+
+ cond_end = new_label();
+ cond_end_st = new_storage(STOR_LABEL);
+ cond_end_st->label = cond_end;
+ cond_end_st->flags = STOR_WANTS_FREE;
+
+ /* evaluate conditional */
+ cond = s2l_gen_expression(expr->conditional);
+ emit_conditional_test(cond, &l_true, &l_false);
+
+ emit_label(l_true->label, "if true");
+
+ /* handle if-true part of the expression */
+ true = s2l_gen_expression(expr->cond_true);
+
+ emit_copy(true, new, expr->ctype);
+
+ emit_conditional_end(l_false, cond_end_st);
+
+ /* handle if-false part of the expression */
+ false = s2l_gen_expression(expr->cond_false);
+
+ emit_copy(false, new, expr->ctype);
+
+ /* end of conditional; jump target for if-true branch */
+ emit_label(cond_end, "end conditional");
+
+ return new;
+}
+
+static struct storage *emit_symbol_expr_init(struct symbol *sym)
+{
+ struct expression *expr = sym->initializer;
+ struct symbol_private *priv = sym->aux;
+
+ if (priv == NULL) {
+ priv = calloc(1, sizeof(*priv));
+ sym->aux = priv;
+
+ if (expr == NULL) {
+ struct storage *new = stack_alloc(4);
+ fprintf(stderr, "FIXME! no value for symbol %s. creating pseudo %d (stack offset %d)\n",
+ show_ident(sym->ident),
+ new->pseudo, new->pseudo * 4);
+ priv->addr = new;
+ } else {
+ priv->addr = s2l_gen_expression(expr);
+ }
+ }
+
+ return priv->addr;
+}
+
+static struct storage *emit_string_expr(struct expression *expr)
+{
+ struct function *f = current_func;
+ int label = new_label();
+ struct storage *new;
+
+ push_cstring(f, expr->string, label);
+
+ new = new_storage(STOR_LABEL);
+ new->label = label;
+ new->flags = STOR_LABEL_VAL | STOR_WANTS_FREE;
+ return new;
+}
+
+static struct storage *emit_cast_expr(struct expression *expr)
+{
+ struct symbol *old_type, *new_type;
+ struct storage *op = s2l_gen_expression(expr->cast_expression);
+ int oldbits, newbits, old_is_signed;
+ struct storage *new;
+ char insnstr[128];
+ char stor_src[16], stor_dest[16];
+
+ old_type = expr->cast_expression->ctype;
+ old_is_signed = type_is_signed(expr->cast_expression->ctype);
+ new_type = expr->cast_type;
+
+ oldbits = old_type->bit_size;
+ newbits = new_type->bit_size;
+ if (oldbits >= newbits)
+ return op;
+
+ new = stack_alloc(newbits / 8);
+
+ strcpy(stor_src, stor_op_name(op));
+ strcpy(stor_dest, stor_op_name(new));
+
+ sprintf(insnstr, "\t%s = %cext i%d %s to i%d\n",
+ stor_dest,
+ old_is_signed ? 's' : 'z',
+ oldbits,
+ stor_src,
+ newbits);
+ push_text_atom(current_func, insnstr);
+
+ return new;
+}
+
+static struct storage *emit_regular_preop(struct expression *expr)
+{
+ struct storage *target = s2l_gen_expression(expr->unop);
+ struct storage *val;
+ int bit_size = expr->unop->ctype->bit_size;
+ struct storage *new;
+ char insnstr[128];
+ char stor_target[16];
+ int lbl_true, lbl_false, lbl_end;
+
+ new = stack_alloc(bit_size / 8);
+
+ strcpy(stor_target, stor_op_name(target));
+
+ switch (expr->op) {
+ case '!':
+ /* compare target with zero */
+ sprintf(insnstr, "\t%%tmp = icmp i%d eq 0, %s\n",
+ bit_size,
+ stor_op_name(target));
+ push_text_atom(current_func, insnstr);
+
+ lbl_true = new_label();
+ lbl_false = new_label();
+ lbl_end = new_label();
+
+ /* if true, goto lbl_true, else goto lbl_false */
+ sprintf(insnstr, "\tbr i1 %%tmp, label %%L%d, label %%L%d\n",
+ lbl_true, lbl_false);
+ push_text_atom(current_func, insnstr);
+
+ /* label lbl_true outputted */
+ emit_label(lbl_true, "preop '!': value is zero");
+
+ /* move constant 1 to result */
+ val = new_storage(STOR_VALUE);
+ val->flags = STOR_WANTS_FREE;
+ val->value = 1;
+
+ emit_move(val, new, expr->unop->ctype, NULL);
+
+ /* jump to end of comparison */
+ sprintf(insnstr, "\tbr label %%L%d\n", lbl_end);
+
+ /* label lbl_false outputted */
+ emit_label(lbl_false, "preop '!': value not zero");
+
+ /* move constant 0 to result */
+ val = new_storage(STOR_VALUE);
+ val->flags = STOR_WANTS_FREE;
+ val->value = 0;
+
+ emit_move(val, new, expr->unop->ctype, NULL);
+
+ /* emit end label */
+ emit_label(lbl_end, "end preop '!'");
+ break;
+
+ case '~':
+ sprintf(insnstr, "\t%s = xor i%d %s, -1\n",
+ stor_op_name(new),
+ bit_size,
+ stor_target);
+ push_text_atom(current_func, insnstr);
+ break;
+
+ case '-':
+ sprintf(insnstr, "\t%s = sub i%d 0, %s\n",
+ stor_op_name(new),
+ bit_size,
+ stor_target);
+ push_text_atom(current_func, insnstr);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return new;
+}
+
+static void emit_case_statement(struct statement *stmt)
+{
+ emit_labelsym(stmt->case_label, NULL);
+ s2l_gen_statement(stmt->case_statement);
+}
+
+static void emit_switch_statement(struct statement *stmt)
+{
+ struct storage *val = s2l_gen_expression(stmt->switch_expression);
+ struct symbol *sym, *default_sym = NULL;
+ struct storage *labelsym;
+ char insnstr[128];
+ char stor_val[16], stor_lbl_def[16], stor_lbl_sym[16];
+
+ FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+ struct statement *case_stmt = sym->stmt;
+ struct expression *expr = case_stmt->case_expression;
+ if (!expr)
+ default_sym = sym;
+ } END_FOR_EACH_PTR(sym);
+
+ if (default_sym)
+ labelsym = new_labelsym(default_sym);
+ else {
+ labelsym = new_storage(STOR_LABEL);
+ labelsym->label = new_label();
+ labelsym->flags = STOR_WANTS_FREE;
+ }
+
+ strcpy(stor_val, stor_op_name(val));
+ strcpy(stor_lbl_def, stor_op_name(labelsym));
+
+ sprintf(insnstr, "\tswitch i%d %s, label %s [\n",
+ val->size * 8,
+ stor_val,
+ stor_lbl_def);
+ push_text_atom(current_func, insnstr);
+
+ FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
+ struct statement *case_stmt = sym->stmt;
+ struct expression *expr = case_stmt->case_expression;
+ /* struct expression *to = case_stmt->case_to; FIXME! */
+
+ /* case NNN: */
+ if (expr) {
+ assert (expr->type == EXPR_VALUE);
+
+ labelsym = new_labelsym(sym);
+ strcpy(stor_lbl_sym, stor_op_name(labelsym));
+
+ sprintf(insnstr, "\t\t\t\ti%d %lld, label %s\n",
+ val->size * 8,
+ expr->value,
+ stor_lbl_sym);
+ push_text_atom(current_func, insnstr);
+ }
+ } END_FOR_EACH_PTR(sym);
+
+ strcpy(insnstr, "\t\t\t\t]\n");
+ push_text_atom(current_func, insnstr);
+
+ s2l_gen_statement(stmt->switch_statement);
+
+ if (stmt->switch_break->used)
+ emit_labelsym(stmt->switch_break, NULL);
+}
+
+static void s2l_gen_struct_member(struct symbol *sym)
+{
+ printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset);
+ printf("\n");
+}
+
+static void s2l_gen_symbol(struct symbol *sym)
+{
+ struct symbol *type;
+
+ if (!sym)
+ return;
+
+ type = sym->ctype.base_type;
+ if (!type)
+ return;
+
+ /*
+ * Show actual implementation information
+ */
+ switch (type->type) {
+
+ case SYM_ARRAY:
+ if (sym->initializer)
+ emit_array(sym);
+ else
+ emit_array_noinit(sym);
+ break;
+
+ case SYM_BASETYPE:
+ if (sym->initializer) {
+ emit_object_pre(show_ident(sym->ident),
+ sym->ctype.modifiers,
+ sym->ctype.alignment,
+ sym->bit_size / 8);
+ emit_scalar(sym->initializer, sym->bit_size);
+ stor_sym_init(sym);
+ } else
+ emit_scalar_noinit(sym);
+ break;
+
+ case SYM_STRUCT:
+ case SYM_UNION: {
+ struct symbol *member;
+
+ printf(" {\n");
+ FOR_EACH_PTR(type->symbol_list, member) {
+ s2l_gen_struct_member(member);
+ } END_FOR_EACH_PTR(member);
+ printf("}\n");
+ break;
+ }
+
+ case SYM_FN: {
+ struct statement *stmt = type->stmt;
+ if (stmt) {
+ emit_func_pre(sym);
+ s2l_gen_statement(stmt);
+ emit_func_post(sym);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (sym->initializer && (type->type != SYM_BASETYPE) &&
+ (type->type != SYM_ARRAY)) {
+ printf(" = \n");
+ s2l_gen_expression(sym->initializer);
+ }
+}
+
+static void s2l_gen_symbol_init(struct symbol *sym);
+
+static void s2l_gen_symbol_decl(struct symbol_list *syms)
+{
+ struct symbol *sym;
+ FOR_EACH_PTR(syms, sym) {
+ s2l_gen_symbol_init(sym);
+ } END_FOR_EACH_PTR(sym);
+}
+
+static void loopstk_push(int cont_lbl, int loop_bottom_lbl)
+{
+ struct function *f = current_func;
+ struct loop_stack *ls;
+
+ ls = malloc(sizeof(*ls));
+ ls->continue_lbl = cont_lbl;
+ ls->loop_bottom_lbl = loop_bottom_lbl;
+ ls->next = f->loop_stack;
+ f->loop_stack = ls;
+}
+
+static void loopstk_pop(void)
+{
+ struct function *f = current_func;
+ struct loop_stack *ls;
+
+ assert(f->loop_stack != NULL);
+ ls = f->loop_stack;
+ f->loop_stack = f->loop_stack->next;
+ free(ls);
+}
+
+static int loopstk_break(void)
+{
+ return current_func->loop_stack->loop_bottom_lbl;
+}
+
+static int loopstk_continue(void)
+{
+ return current_func->loop_stack->continue_lbl;
+}
+
+static void emit_loop(struct statement *stmt)
+{
+ struct statement *pre_statement = stmt->iterator_pre_statement;
+ struct expression *pre_condition = stmt->iterator_pre_condition;
+ struct statement *statement = stmt->iterator_statement;
+ struct statement *post_statement = stmt->iterator_post_statement;
+ struct expression *post_condition = stmt->iterator_post_condition;
+ int loop_top = 0, loop_bottom, loop_continue;
+ int have_bottom = 0, dummy;
+ struct storage *val, *tmp;
+ char insnstr[128];
+ char stor_val[16], stor_tmp[16], stor_lbv[16];
+
+ loop_bottom = new_label();
+ loop_continue = new_label();
+ loopstk_push(loop_continue, loop_bottom);
+
+ s2l_gen_symbol_decl(stmt->iterator_syms);
+ s2l_gen_statement(pre_statement);
+
+ loop_top = new_label();
+ emit_label(loop_top, "loop top");
+
+ if (pre_condition) {
+ if (pre_condition->type == EXPR_VALUE) {
+ if (!pre_condition->value) {
+ struct storage *lbv;
+ lbv = new_storage(STOR_LABEL);
+ lbv->label = loop_bottom;
+ lbv->flags = STOR_WANTS_FREE;
+
+ sprintf(insnstr, "\tbr label %s\n",
+ stor_op_name(lbv));
+ push_text_atom(current_func, insnstr);
+
+ have_bottom = 1;
+ }
+ } else {
+ struct storage *lbv;
+
+ lbv = new_storage(STOR_LABEL);
+ lbv->label = loop_bottom;
+ lbv->flags = STOR_WANTS_FREE;
+ have_bottom = 1;
+
+ val = s2l_gen_expression(pre_condition);
+
+ tmp = stack_alloc(val->size);
+
+ strcpy(stor_val, stor_op_name(val));
+ strcpy(stor_tmp, stor_op_name(tmp));
+ strcpy(stor_lbv, stor_op_name(lbv));
+
+ sprintf(insnstr, "\t%s = icmp eq i%d 0, %s\n",
+ stor_tmp,
+ tmp->size * 8,
+ stor_val);
+ push_text_atom(current_func, insnstr);
+
+ dummy = new_label();
+
+ sprintf(insnstr, "\tbr i1 %s, label %s, label %%L%d\n",
+ stor_tmp,
+ stor_lbv,
+ dummy);
+ push_text_atom(current_func, insnstr);
+
+ emit_label(dummy, NULL);
+ }
+ }
+
+ s2l_gen_statement(statement);
+ if (stmt->iterator_continue->used)
+ emit_label(loop_continue, "'continue' iterator");
+ s2l_gen_statement(post_statement);
+ if (!post_condition) {
+ struct storage *lbv = new_storage(STOR_LABEL);
+ lbv->label = loop_top;
+ lbv->flags = STOR_WANTS_FREE;
+
+ sprintf(insnstr, "\tbr label %s\n", stor_op_name(lbv));
+ push_text_atom(current_func, insnstr);
+ } else if (post_condition->type == EXPR_VALUE) {
+ if (post_condition->value) {
+ struct storage *lbv = new_storage(STOR_LABEL);
+ lbv->label = loop_top;
+ lbv->flags = STOR_WANTS_FREE;
+
+ sprintf(insnstr, "\tbr label %s\n", stor_op_name(lbv));
+ push_text_atom(current_func, insnstr);
+ }
+ } else {
+ struct storage *lbv = new_storage(STOR_LABEL);
+ lbv->label = loop_top;
+ lbv->flags = STOR_WANTS_FREE;
+
+ val = s2l_gen_expression(post_condition);
+
+ tmp = stack_alloc(val->size);
+
+ strcpy(stor_val, stor_op_name(val));
+ strcpy(stor_tmp, stor_op_name(tmp));
+ strcpy(stor_lbv, stor_op_name(lbv));
+
+ sprintf(insnstr, "\t%s = icmp eq i%d 0, %s\n",
+ stor_tmp,
+ tmp->size * 8,
+ stor_val);
+ push_text_atom(current_func, insnstr);
+
+ dummy = new_label();
+
+ sprintf(insnstr, "\tbr i1 %s, label %s, label %%L%d\n",
+ stor_tmp,
+ stor_lbv,
+ dummy);
+ push_text_atom(current_func, insnstr);
+
+ emit_label(dummy, NULL);
+ }
+ if (have_bottom || stmt->iterator_break->used)
+ emit_label(loop_bottom, "loop bottom");
+
+ loopstk_pop();
+}
+
+static void emit_goto_statement(struct statement *stmt)
+{
+ char insnstr[128];
+
+ insnstr[0] = 0;
+
+ if (stmt->goto_expression) {
+ struct storage *val = s2l_gen_expression(stmt->goto_expression);
+ printf("\tFIXME goto *v%d\n", val->pseudo);
+ }
+
+ else if (!strcmp("break", show_ident(stmt->goto_label->ident))) {
+ struct storage *lbv = new_storage(STOR_LABEL);
+ lbv->label = loopstk_break();
+ lbv->flags = STOR_WANTS_FREE;
+
+ sprintf(insnstr, "\tbr label %s\n", stor_op_name(lbv));
+ }
+
+ else if (!strcmp("continue", show_ident(stmt->goto_label->ident))) {
+ struct storage *lbv = new_storage(STOR_LABEL);
+ lbv->label = loopstk_continue();
+ lbv->flags = STOR_WANTS_FREE;
+
+ sprintf(insnstr, "\tbr label %s\n", stor_op_name(lbv));
+ }
+
+ else {
+ struct storage *labelsym = new_labelsym(stmt->goto_label);
+
+ sprintf(insnstr, "\tbr label %s\n", stor_op_name(labelsym));
+ }
+
+ if (insnstr[0])
+ push_text_atom(current_func, insnstr);
+}
+
+/*
+ * Print out a statement
+ */
+static struct storage *s2l_gen_statement(struct statement *stmt)
+{
+ if (!stmt)
+ return NULL;
+ switch (stmt->type) {
+ default:
+ return NULL;
+ case STMT_RETURN:
+ return emit_return_stmt(stmt);
+ case STMT_DECLARATION:
+ s2l_gen_symbol_decl(stmt->declaration);
+ break;
+ case STMT_COMPOUND: {
+ struct statement *s;
+ struct storage *last = NULL;
+
+ FOR_EACH_PTR(stmt->stmts, s) {
+ last = s2l_gen_statement(s);
+ } END_FOR_EACH_PTR(s);
+
+ return last;
+ }
+
+ case STMT_EXPRESSION:
+ return s2l_gen_expression(stmt->expression);
+ case STMT_IF:
+ emit_if_conditional(stmt);
+ return NULL;
+
+ case STMT_CASE:
+ emit_case_statement(stmt);
+ break;
+ case STMT_SWITCH:
+ emit_switch_statement(stmt);
+ break;
+
+ case STMT_ITERATOR:
+ emit_loop(stmt);
+ break;
+
+ case STMT_NONE:
+ break;
+
+ case STMT_LABEL:
+ printf(".L%p:\n", stmt->label_identifier);
+ s2l_gen_statement(stmt->label_statement);
+ break;
+
+ case STMT_GOTO:
+ emit_goto_statement(stmt);
+ break;
+ case STMT_ASM:
+ printf("\tasm( .... )\n");
+ break;
+ }
+ return NULL;
+}
+
+static struct storage *s2l_gen_call_expression(struct expression *expr)
+{
+ struct symbol *direct;
+ struct expression *arg, *fn;
+ struct storage *retval, *fncall;
+ int first_arg = 1;
+ char s[64];
+ char arg_str[1024];
+ char callstr[1024];
+
+ if (!expr->ctype) {
+ warning(expr->pos, "\tcall with no type!");
+ return NULL;
+ }
+
+ retval = stack_alloc(expr->ctype->bit_size / 8);
+
+ arg_str[0] = 0;
+
+ sprintf(callstr, "\t%s = call i%d ",
+ stor_op_name(retval),
+ expr->ctype->bit_size);
+
+ FOR_EACH_PTR(expr->args, arg) {
+ struct storage *new = s2l_gen_expression(arg);
+ int size = arg->ctype->bit_size;
+
+ /*
+ * FIXME: i386 SysV ABI dictates that values
+ * smaller than 32 bits should be placed onto
+ * the stack as 32-bit objects. We should not
+ * blindly do a 32-bit push on objects smaller
+ * than 32 bits.
+ */
+ if (size < 32)
+ size = 32;
+
+ sprintf(s, "%si%d %s",
+ first_arg ? "" : ", ",
+ size,
+ stor_op_name(new));
+
+ strcat(arg_str, s);
+
+ first_arg = 0;
+ } END_FOR_EACH_PTR(arg);
+
+ fn = expr->fn;
+
+ /* Remove dereference, if any */
+ direct = NULL;
+ if (fn->type == EXPR_PREOP) {
+ if (fn->unop->type == EXPR_SYMBOL) {
+ struct symbol *sym = fn->unop->symbol;
+ if (sym->ctype.base_type->type == SYM_FN)
+ direct = sym;
+ }
+ }
+ if (direct) {
+ struct storage *direct_stor = new_storage(STOR_SYM);
+ direct_stor->flags |= STOR_WANTS_FREE;
+ direct_stor->sym = direct;
+
+ sprintf(s, "@%s(", stor_op_name(direct_stor));
+ strcat(callstr, s);
+ } else {
+ fncall = s2l_gen_expression(fn);
+
+ sprintf(s, "%%%s(", stor_op_name(fncall));
+ strcat(callstr, s);
+ }
+
+ strcat(callstr, arg_str);
+ strcat(callstr, ")\n");
+ push_text_atom(current_func, callstr);
+
+ return retval;
+}
+
+static struct storage *s2l_gen_address_gen(struct expression *expr)
+{
+ struct storage *addr;
+ struct storage *new;
+ int bits = expr->ctype->bit_size;
+ char insnstr[128];
+ char stor_addr[16], stor_new[16];
+
+ addr = s2l_gen_expression(expr->unop);
+ if (expr->unop->type == EXPR_SYMBOL)
+ return addr;
+
+ new = stack_alloc(bits / 8);
+
+ strcpy(stor_new, stor_op_name(new));
+ strcpy(stor_addr, stor_op_name(addr));
+
+ sprintf(insnstr, "\t%s = load i%d* %s\n",
+ stor_new,
+ bits,
+ stor_addr);
+ push_text_atom(current_func, insnstr);
+
+ return new;
+}
+
+static struct storage *s2l_gen_assignment(struct expression *expr)
+{
+ struct expression *target = expr->left;
+ struct storage *val, *addr;
+
+ if (!expr->ctype)
+ return NULL;
+
+ val = s2l_gen_expression(expr->right);
+ addr = s2l_gen_address_gen(target);
+
+ switch (val->type) {
+ /* copy, where both operands are memory */
+ case STOR_PSEUDO:
+ case STOR_ARG:
+ emit_copy(val, addr, expr->ctype);
+ break;
+
+ /* copy, one or zero operands are memory */
+ case STOR_SYM:
+ case STOR_VALUE:
+ case STOR_LABEL:
+ emit_move(val, addr, expr->left->ctype, NULL);
+ break;
+
+ case STOR_LABELSYM:
+ assert(0);
+ break;
+ }
+ return val;
+}
+
+static int s2l_gen_initialization(struct symbol *sym, struct expression *expr)
+{
+ struct storage *val, *addr;
+ int bits;
+
+ if (!expr->ctype)
+ return 0;
+
+ bits = expr->ctype->bit_size;
+ val = s2l_gen_expression(expr);
+ addr = s2l_gen_symbol_expr(sym);
+ // FIXME! The "target" expression is for bitfield store information.
+ // Leave it NULL, which works fine.
+ emit_store(NULL, addr, val, bits);
+ return 0;
+}
+
+static struct storage *s2l_gen_access(struct expression *expr)
+{
+ return s2l_gen_address_gen(expr);
+}
+
+static struct storage *s2l_gen_preop(struct expression *expr)
+{
+ /*
+ * '*' is an lvalue access, and is fundamentally different
+ * from an arithmetic operation. Maybe it should have an
+ * expression type of its own..
+ */
+ if (expr->op == '*')
+ return s2l_gen_access(expr);
+ if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
+ return emit_inc_dec(expr, 0);
+ return emit_regular_preop(expr);
+}
+
+static struct storage *s2l_gen_symbol_expr(struct symbol *sym)
+{
+ struct storage *new = stack_alloc(4);
+
+ if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC)) {
+ printf("\tmovi.%d\t\tv%d,$%s\n", bits_in_pointer, new->pseudo, show_ident(sym->ident));
+ return new;
+ }
+ if (sym->ctype.modifiers & MOD_ADDRESSABLE) {
+ printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new->pseudo, sym->value);
+ return new;
+ }
+ printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new->pseudo, show_ident(sym->ident), sym);
+ return new;
+}
+
+static void s2l_gen_symbol_init(struct symbol *sym)
+{
+ struct symbol_private *priv = sym->aux;
+ struct expression *expr = sym->initializer;
+ struct storage *new;
+
+ new = stack_alloc(sym->bit_size / 8);
+ if (expr) {
+ struct storage *val;
+
+ val = s2l_gen_expression(expr);
+
+ emit_copy(val, new, expr->ctype);
+ }
+
+ if (!priv) {
+ priv = calloc(1, sizeof(*priv));
+ sym->aux = priv;
+ /* FIXME: leak! we don't free... */
+ /* (well, we don't free symbols either) */
+ }
+
+ priv->addr = new;
+}
+
+static int type_is_signed(struct symbol *sym)
+{
+ if (sym->type == SYM_NODE)
+ sym = sym->ctype.base_type;
+ if (sym->type == SYM_PTR)
+ return 0;
+ return !(sym->ctype.modifiers & MOD_UNSIGNED);
+}
+
+static struct storage *s2l_gen_label_expr(struct expression *expr)
+{
+ struct storage *new = stack_alloc(4);
+ printf("\tmovi.%d\t\tv%d,.L%p\n", bits_in_pointer, new->pseudo, expr->label_symbol);
+ return new;
+}
+
+static struct storage *s2l_gen_statement_expr(struct expression *expr)
+{
+ return s2l_gen_statement(expr->statement);
+}
+
+static int s2l_gen_position_expr(struct expression *expr, struct symbol *base)
+{
+ struct storage *new = s2l_gen_expression(expr->init_expr);
+ struct symbol *ctype = expr->init_expr->ctype;
+
+ printf("\tinsert v%d at [%d:%d] of %s\n", new->pseudo,
+ expr->init_offset, ctype->bit_offset,
+ show_ident(base->ident));
+ return 0;
+}
+
+static void s2l_gen_initializer_expr(struct expression *expr, struct symbol *ctype)
+{
+ struct expression *entry;
+
+ FOR_EACH_PTR(expr->expr_list, entry) {
+ // Nested initializers have their positions already
+ // recursively calculated - just output them too
+ if (entry->type == EXPR_INITIALIZER) {
+ s2l_gen_initializer_expr(entry, ctype);
+ continue;
+ }
+
+ // Ignore initializer indexes and identifiers - the
+ // evaluator has taken them into account
+ if (entry->type == EXPR_IDENTIFIER || entry->type == EXPR_INDEX)
+ continue;
+ if (entry->type == EXPR_POS) {
+ s2l_gen_position_expr(entry, ctype);
+ continue;
+ }
+ s2l_gen_initialization(ctype, entry);
+ } END_FOR_EACH_PTR(entry);
+}
+
+/*
+ * Print out an expression. Return the pseudo that contains the
+ * variable.
+ */
+static struct storage *s2l_gen_expression(struct expression *expr)
+{
+ if (!expr)
+ return NULL;
+
+ if (!expr->ctype) {
+ struct position *pos = &expr->pos;
+ printf("\tno type at %s:%d:%d\n",
+ stream_name(pos->stream),
+ pos->line, pos->pos);
+ return NULL;
+ }
+
+ switch (expr->type) {
+ default:
+ return NULL;
+ case EXPR_CALL:
+ return s2l_gen_call_expression(expr);
+
+ case EXPR_ASSIGNMENT:
+ return s2l_gen_assignment(expr);
+
+ case EXPR_COMPARE:
+ return emit_compare(expr);
+ case EXPR_BINOP:
+ case EXPR_COMMA:
+ case EXPR_LOGICAL:
+ return emit_binop(expr);
+ case EXPR_PREOP:
+ return s2l_gen_preop(expr);
+ case EXPR_POSTOP:
+ return emit_postop(expr);
+ case EXPR_SYMBOL:
+ return emit_symbol_expr_init(expr->symbol);
+ case EXPR_DEREF:
+ case EXPR_SIZEOF:
+ case EXPR_ALIGNOF:
+ warning(expr->pos, "invalid expression after evaluation");
+ return NULL;
+ case EXPR_CAST:
+ case EXPR_FORCE_CAST:
+ case EXPR_IMPLIED_CAST:
+ return emit_cast_expr(expr);
+ case EXPR_VALUE:
+ return emit_value(expr);
+ case EXPR_STRING:
+ return emit_string_expr(expr);
+ case EXPR_INITIALIZER:
+ s2l_gen_initializer_expr(expr, expr->ctype);
+ return NULL;
+ case EXPR_SELECT:
+ case EXPR_CONDITIONAL:
+ return emit_conditional_expr(expr);
+ case EXPR_STATEMENT:
+ return s2l_gen_statement_expr(expr);
+ case EXPR_LABEL:
+ return s2l_gen_label_expr(expr);
+
+ // None of these should exist as direct expressions: they are only
+ // valid as sub-expressions of initializers.
+ case EXPR_POS:
+ warning(expr->pos, "unable to show plain initializer position expression");
+ return NULL;
+ case EXPR_IDENTIFIER:
+ warning(expr->pos, "unable to show identifier expression");
+ return NULL;
+ case EXPR_INDEX:
+ warning(expr->pos, "unable to show index expression");
+ return NULL;
+ case EXPR_TYPE:
+ warning(expr->pos, "unable to show type expression");
+ return NULL;
+ case EXPR_FVALUE:
+ warning(expr->pos, "floating point support is not implemented");
+ return NULL;
+ }
+ return NULL;
+}
diff --git a/s2l.c b/s2l.c
new file mode 100644
index 0000000..adba48a
--- /dev/null
+++ b/s2l.c
@@ -0,0 +1,77 @@
+/*
+ * client program that uses the sparse library and LLVM.
+ *
+ * Copyright (C) 2003 Transmeta Corp.
+ * 2003 Linus Torvalds
+ * Copyright 2003 Jeff Garzik
+ * Copyright 2009 Red Hat, Inc.
+ *
+ * Licensed under the Open Software License version 1.1
+ *
+ *
+ * Example usage:
+ *
+ * ./s2l -I/usr/local/include -DHARPSICHORD myfile.c > myfile.ll
+ * llvm-as myfile.ll # produces myfile.bc
+ * llc myfile.bc # produces myfile.s, target-specific asm
+ * as -o myfile.o myfile.s # GNU assembler for the final step
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "lib.h"
+#include "allocate.h"
+#include "token.h"
+#include "parse.h"
+#include "symbol.h"
+#include "expression.h"
+#include "compile.h"
+
+static void clean_up_symbols(struct symbol_list *list)
+{
+ struct symbol *sym;
+
+ FOR_EACH_PTR(list, sym) {
+ expand_symbol(sym);
+ emit_one_symbol(sym);
+ } END_FOR_EACH_PTR(sym);
+}
+
+int main(int argc, char **argv)
+{
+ char *file;
+ struct string_list *filelist = NULL;
+
+ clean_up_symbols(sparse_initialize(argc, argv, &filelist));
+ add_pre_buffer("#define __x86_64__ 1\n");
+ FOR_EACH_PTR_NOTAG(filelist, file) {
+ struct symbol_list *list;
+ const char *basename = strrchr(file, '/');
+ basename = basename ? basename+1 : file;
+
+ list = sparse(file);
+
+ // Do type evaluation and simplification
+ emit_unit_begin(basename);
+ clean_up_symbols(list);
+ emit_unit_end();
+ } END_FOR_EACH_PTR_NOTAG(file);
+
+#if 0
+ // And show the allocation statistics
+ show_ident_alloc();
+ show_token_alloc();
+ show_symbol_alloc();
+ show_expression_alloc();
+ show_statement_alloc();
+ show_string_alloc();
+ show_bytes_alloc();
+#endif
+ return 0;
+}