Scripting and control environment for stage lighting
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1331 lines
28 KiB

/* code.c */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <err.h>
#include <limits.h>
#include "types.h"
#include "ast.h"
#include "codegen.h"
#include "code.h"
#include "mem.h"
#include "hash.h"
struct var_desc {
int type;
int addr;
int flags;
char *name;
};
struct fn_desc {
int type;
int num;
char *name;
};
struct label {
int value;
char *name;
};
#define DEBUG 1
#define MOREDEBUG 0
#define MAX_LABELS 4096
#define MAX_CONSTS 4096
struct label *labels = NULL;
int nlabels = 0;
int maxlabels = 0;
int *consts = NULL;
int nconsts = 0;
int maxconsts = 0;
FILE *binout = NULL;
int sp = 0;
int consume = 1;
int real = 0;
int switchmin;
int *switchlabels = NULL;
int switchdefault;
int breaklabel;
int output_asm = 1;
void output_int(int);
void output_byte(int);
void output_string(char *);
int get_label(int);
#define IF_ISINSTR 0x01
#define IF_ISLABEL 0x02
#define IF_ADDLABEL 0x04
#define IF_ADDCONST 0x08
#define IF_HASOPERAND 0x10
#define IF_HASSTRING 0x20
#define IF_SUBPC 0x40
#define IF_FIXEDSIZE 0x80
struct instr {
struct instr *next;
int opcode;
int operand;
char *string;
int label;
int constant;
int bytes;
int flags;
};
struct instr *instr_head = NULL;
struct instr *instr_tail = NULL;
#define VAR_LOCAL 1
#define VAR_GLOBAL 2
#define VAR_ARRAY 4
#define VAR_ARG 1
#define VAR_REAL 2
#define FN_INT 1
#define FN_STR 2
#define FN_LOCAL 3
int fnconst;
int nargs;
int local_variable_count = -1;
int local_arg_count = -1;
int function_count = -1;
int constant_count = -1;
#define HASHSIZE 512
#define HASHMASK (HASHSIZE - 1)
struct hashentry *varhash[HASHSIZE];
struct hashentry *fnhash[HASHSIZE];
struct hashentry *constanthash[HASHSIZE];
void compiler_error(char *str)
{
errx(1, str);
}
int count_local_variables(void)
{
return local_variable_count;
}
void dump_local_variables_action(struct hashentry *ptr)
{
printf("%%var %s %d %d\n", ptr->name, ptr->value, ptr->flags);
}
void dump_local_variables(void)
{
hash_iterate(varhash, dump_local_variables_action);
}
void output_functions_action(struct hashentry *ptr)
{
int len, pad;
if (ptr->flags == FN_LOCAL) {
len = strlen(ptr->name) + 1 + 16;
pad = (4 - (len % 4)) % 4;
output_int(len+pad);
output_int(0); /* type */
output_int(get_label(ptr->value) + 28);
output_int(0); /* nargs */
output_string(ptr->name);
output_byte(0); /* terminator */
while (pad--)
output_byte(0);
}
}
void output_functions(void)
{
hash_iterate(fnhash, output_functions_action);
output_int(0); /* terminator */
}
void reset_local_variables(void)
{
if (local_variable_count >= 0)
hash_clear(varhash);
hash_init(varhash);
local_variable_count = 0;
local_arg_count = 0;
}
void reset_functions(void)
{
if (function_count >= 0)
hash_clear(fnhash);
hash_init(fnhash);
function_count = 0;
}
void reset_constants(void)
{
if (constant_count >= 0)
hash_clear(constanthash);
hash_init(constanthash);
constant_count = 0;
}
int lookup_variable(ast *node, struct var_desc *var, int create, int flags)
{
char *varname;
struct hashentry *hashptr;
assert((node->tag == var_ast) || ((node->tag == array_ast) &&
(node->info.node.tag = kind_array)));
var->type = 0;
if (node->tag == var_ast)
varname = node->info.variable;
else {
varname = node->info.node.head->elem->info.variable;
var->type |= VAR_ARRAY;
}
assert(varname != NULL);
assert(varname[0] != '\0');
var->name = varname;
if (local_variable_count < 0)
reset_local_variables();
switch (varname[0]) {
case '$':
var->type |= VAR_GLOBAL;
var->flags = 0;
break;
case '%':
flags |= VAR_REAL;
default:
var->type |= VAR_LOCAL;
hashptr = hash_lookup(varhash, varname, create);
if (!hashptr)
return 0;
if (hashptr->name == NULL) {
hashptr->name = varname;
hashptr->flags = flags;
if (flags & VAR_ARG)
hashptr->value = local_arg_count++;
else
hashptr->value = local_variable_count++;
}
var->addr = hashptr->value;
var->flags = hashptr->flags;
printf("var %s, flags %d\n", varname, var->flags);
fflush(stdout);
break;
}
return 1;
}
int lookup_function(ast *node, struct fn_desc *fn)
{
char *fnname = node->info.string;
struct hashentry *hashptr;
fn->name = fnname;
if (function_count < 0)
reset_functions();
hashptr = hash_lookup(fnhash, fnname, 0);
if (!hashptr)
return 0;
fn->type = hashptr->flags;
fn->num = hashptr->value;
return 1;
}
#define lookup_constant(node, constant) \
lookup_constant_string(node->info.string, constant)
int lookup_constant_string(char *constantname, int *constant)
{
struct hashentry *hashptr;
if (constant_count < 0)
reset_constants();
hashptr = hash_lookup(constanthash, constantname, 0);
if (!hashptr)
return 0;
*constant = hashptr->value;
return 1;
}
void create_function(char *fnname, int type, int num)
{
struct hashentry *hashptr;
if (function_count < 0)
reset_functions();
hashptr = hash_lookup(fnhash, fnname, 1);
if (hashptr->name)
compiler_error("Function already exists");
if (output_asm) {
switch (type) {
case FN_INT:
printf("%%fnint %s %d\n", fnname, num);
break;
case FN_STR:
printf("%%fnext %s\n", fnname);
break;
case FN_LOCAL:
printf("%%fnlocal %s lab_%d\n", fnname, num);
break;
}
}
hashptr->name = fnname;
hashptr->flags = type;
hashptr->value = num;
}
void create_constant(char *constantname, int value)
{
struct hashentry *hashptr;
if (constant_count < 0)
reset_constants();
hashptr = hash_lookup(constanthash, constantname, 1);
if (hashptr->name)
compiler_error("Constant already defined");
hashptr->name = constantname;
hashptr->value = value;
}
int newlabel(char *name)
{
if (nlabels >= maxlabels) {
/* We need to allocate more label space */
if (nlabels != 0) {
/* Error for now */
compiler_error("Too many labels");
}
maxlabels = MAX_LABELS;
labels = safe_malloc(maxlabels * sizeof(struct label));
}
labels[nlabels].value = -1;
labels[nlabels].name = name;
return nlabels++;
}
int newconst(void)
{
if (nconsts >= maxconsts) {
/* We need to allocate more const space */
if (nconsts != 0) {
/* Error for now */
compiler_error("Too many consts");
}
maxconsts = MAX_CONSTS;
consts = safe_malloc(maxconsts * sizeof(int));
}
consts[nconsts] = 0;
return nconsts++;
}
void set_label(int label, int value)
{
labels[label].value = value;
}
void set_const(int constant, int value)
{
if (output_asm)
printf("%%const const_%d %d\n", constant, value);
consts[constant] = value;
}
int get_label(int label)
{
return labels[label].value;
}
int get_const(int constant)
{
return consts[constant];
}
#define INSTR_ADD(i) i->next = NULL; \
if (instr_tail) \
instr_tail->next = i; \
else \
instr_head = i; \
instr_tail = i
void emit_simple_instr(int opcode)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->bytes = 1;
i->flags = IF_ISINSTR;
if (output_asm)
printf("\t%s\n", INSTR_NAME(opcode));
INSTR_ADD(i);
}
void emit_instr_immediate(int opcode, int operand)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->operand = operand;
i->bytes = 2; /* Start at 2 */
i->flags = IF_ISINSTR | IF_HASOPERAND;
if (output_asm)
printf("\t%s %d\n", INSTR_NAME(opcode), operand);
INSTR_ADD(i);
}
void emit_instr_string(int opcode, char *string)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->string = string;
i->bytes = strlen(string) + 2;
i->flags = IF_ISINSTR | IF_HASSTRING;
if (output_asm)
printf("\t%s \"%s\"\n", INSTR_NAME(opcode), string);
INSTR_ADD(i);
}
void emit_instr_imm_const(int opcode, int operand, int constant)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->operand = operand;
i->constant = constant;
i->bytes = 2; /* Start at 2 */
i->flags = IF_ISINSTR | IF_HASOPERAND | IF_ADDCONST;
if (output_asm)
printf("\t%s %d + const_%d\n", INSTR_NAME(opcode), operand,
constant);
INSTR_ADD(i);
}
void emit_instr_label(int opcode, int label)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->operand = 0;
i->label = label;
i->bytes = 2; /* Start at 2 */
i->flags = IF_ISINSTR | IF_HASOPERAND | IF_ADDLABEL | IF_SUBPC;
if (output_asm)
printf("\t%s lab_%d\n", INSTR_NAME(opcode), label);
INSTR_ADD(i);
}
void emit_instr_label_sized(int opcode, int label, int size)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->opcode = opcode;
i->operand = 0;
i->label = label;
i->bytes = size;
i->flags = IF_ISINSTR | IF_HASOPERAND | IF_ADDLABEL | IF_SUBPC
| IF_FIXEDSIZE;
if (output_asm)
printf("\t%s lab_%d\n", INSTR_NAME(opcode), label);
INSTR_ADD(i);
}
void emit_label(int label)
{
struct instr *i;
i = safe_malloc(sizeof(struct instr));
i->flags = IF_ISLABEL;
i->label = label;
i->bytes = 0;
if (output_asm)
printf("lab_%d:\n", label);
INSTR_ADD(i);
}
void codegen(ast *node)
{
struct var_desc var;
struct fn_desc fn;
int len;
ast_list *ptr;
int savedsp;
int savedreal;
int label, label2;
int localconsume;
int num;
int max;
int range;
int savedswitchmin;
int *savedswitchlabels;
int savedswitchdefault;
int savedbreaklabel;
int switchend;
int stackfixlabel;
int hasdefault;
int i;
union {
int i;
float f;
} conv;
#if DEBUG
printf("entering codegen with node %p, sp = %d, tag = %d\n", node, sp, node->tag);
if (node->tag == node_ast)
printf("kind == %d\n", node->info.node.tag);
fflush(stdout);
#endif
switch (node->tag) {
case int_ast:
emit_instr_immediate(OP_PUSH, node->info.integer);
real = 0;
sp++;
break;
case real_ast:
conv.f = node->info.real; /* XXX Not exactly MI */
emit_instr_immediate(OP_PUSH, conv.i);
real = 1;
sp++;
break;
case casenum_ast:
num = node->info.integer;
num = num - switchmin;
emit_label(switchlabels[num]);
break;
case casevar_ast:
lookup_constant(node, &num);
num = num - switchmin;
emit_label(switchlabels[num]);
break;
case var_ast:
case array_ast:
if (!lookup_variable(node, &var, 0, 0)) {
/*
* If the var doesn't exist, check for a constant
* If we find one, great. Deal with it, and get
* on with the rest of the tree. If not, bail.
*/
if (lookup_constant(node, &num)) {
emit_instr_immediate(OP_PUSH, num);
sp++;
real = (*node->info.string == '%');
break;
}
printf("error: variable '%s' used before assignment\n",
var.name);
exit(EXIT_FAILURE);
}
// printf("sp = %d\n", sp);
if (var.flags & VAR_REAL)
real = 1;
else
real = 0;
switch (var.type) {
case VAR_LOCAL:
if (var.flags & VAR_ARG) {
/*
* Arguments on the stack directly above
* local variables, and then return address
*/
emit_instr_imm_const(OP_LOAD,
sp + 1 + nargs - var.addr, fnconst);
} else {
emit_instr_imm_const(OP_LOAD, sp - var.addr - 1, fnconst);
}
sp++;
break;
case VAR_LOCAL | VAR_ARRAY:
compiler_error("Local arrays not supported");
break;
case VAR_GLOBAL:
emit_instr_string(OP_PUSHSTR, var.name);
emit_instr_immediate(OP_CALLNUM, INTFN_GLOBAL_LOAD);
sp++;
break;
case VAR_GLOBAL | VAR_ARRAY:
codegen(node->info.node.head->next->elem);
emit_instr_string(OP_PUSHSTR, var.name);
emit_instr_immediate(OP_CALLNUM,
INTFN_GLOBAL_ARRAY_LOAD);
/* No sp++ because codegen has already done it */
break;
}
break;
case str_ast:
len = strlen(node->info.string);
emit_instr_string(OP_PUSHSTR, node->info.string);
sp+=(len+sizeof(stkentry)-1) / sizeof(stkentry) + 1;
break;
case node_ast:
switch (node->info.node.tag) {
case kind_fndefint:
assert(sp == 0);
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
create_function(node->info.node.head->elem->info.string,
FN_INT,
node->info.node.head->next->elem->info.integer);
break;
case kind_constant:
assert(sp == 0);
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
create_constant(node->info.node.head->elem->info.string,
node->info.node.head->next->elem->info.integer);
break;
case kind_fndefext:
assert(sp == 0);
assert(node->info.node.head != NULL);
create_function(node->info.node.head->elem->info.string,
FN_STR, 0);
break;
case kind_fndef:
assert(sp == 0);
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next != NULL);
label =
newlabel(node->info.node.head->elem->info.string);
emit_label(label);
/* Set up the arguments in the symbol table */
nargs = 0;
for (ptr =
node->info.node.head->next->elem->info.node.head;
ptr; ptr = ptr->next) {
lookup_variable(ptr->elem, &var, 1, VAR_ARG);
nargs++;
}
/* Should store the number of args somewhere */
create_function(node->info.node.head->elem->info.string,
FN_LOCAL, label);
/* Allocate space on stack for local variables */
fnconst = newconst();
emit_instr_imm_const(OP_ALLOC, 0, fnconst);
/* Evaluate the contents */
codegen(node->info.node.head->next->next->elem);
/* Restore the stack and return */
emit_instr_imm_const(OP_POP, 0, fnconst);
emit_simple_instr(OP_RET);
/* And set the constant */
set_const(fnconst, count_local_variables());
if (output_asm)
dump_local_variables();
reset_local_variables();
break;
case kind_assign:
/* We have a list with an lvalue and an expression */
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next == NULL);
ptr = node->info.node.head->elem->info.node.head;
consume = 0;
while (ptr) {
consume++;
ptr = ptr->next;
}
if (consume < 1)
consume = 1;
/* Evaluate the expression first */
codegen(node->info.node.head->next->elem);
ptr = node->info.node.head->elem->info.node.head;
if (ptr == NULL) {
/* Consume expression */
emit_instr_immediate(OP_POP, 1);
sp--;
}
while (ptr) {
lookup_variable(ptr->elem, &var, 1, 0);
switch (var.type) {
case VAR_LOCAL:
emit_instr_imm_const(OP_STORE,
sp - var.addr - 2, fnconst);
sp--;
break;
case VAR_LOCAL | VAR_ARRAY:
compiler_error("Local arrays not supported");
break;
case VAR_GLOBAL:
emit_instr_string(OP_PUSHSTR, var.name);
emit_instr_immediate(OP_CALLNUM,
INTFN_GLOBAL_STORE);
sp--;
break;
case VAR_GLOBAL | VAR_ARRAY:
codegen(ptr->elem->info.node.head->next->elem);
emit_instr_string(OP_PUSHSTR, var.name);
emit_instr_immediate(OP_CALLNUM,
INTFN_GLOBAL_ARRAY_STORE);
sp--;
sp--;
break;
}
ptr = ptr->next;
}
break;
case kind_list:
for (ptr = node->info.node.head; ptr; ptr = ptr->next)
codegen(ptr->elem);
break;
case kind_call:
/* We have a function name and a list of arguments */
assert(node->info.node.head != NULL);
savedsp = sp;
localconsume = consume;
consume = 1;
// printf("savedsp = %d\n", savedsp);
/* Evaluate the arguments first */
for (ptr =
node->info.node.head->next->elem->info.node.head;
ptr; ptr = ptr->next) {
codegen(ptr->elem);
}
// printf("sp = %d\n", sp);
emit_instr_immediate(OP_ALLOC, 1);
sp++;
if (!lookup_function(node->info.node.head->elem, &fn))
compiler_error("Function not found");
switch (fn.type) {
case FN_INT:
emit_instr_immediate(OP_CALLNUM, fn.num);
break;
case FN_STR:
emit_instr_string(OP_CALLSTR, fn.name);
break;
case FN_LOCAL:
emit_instr_label(OP_BL, fn.num);
break;
}
// printf("sp = %d, savedsp = %d\n", sp, savedsp);
if (sp > (savedsp + localconsume)) {
emit_instr_immediate(OP_STORE, sp - savedsp
- localconsume - 1);
if (sp > (savedsp + localconsume + 1)) {
emit_instr_immediate(OP_POP,
sp-savedsp - localconsume - 1);
}
}
// printf("localconsume = %d\n", localconsume);
sp = savedsp + localconsume;
break;
#define BINOP assert(node->info.node.head != NULL); \
assert(node->info.node.head->next != NULL); \
assert(node->info.node.head->next->next == NULL); \
codegen(node->info.node.head->elem); \
savedreal = real; \
codegen(node->info.node.head->next->elem); \
if (savedreal != real) \
compiler_error("Type mismatch");
case op_plus:
BINOP
if (real)
emit_simple_instr(FLOP_ADD);
else
emit_simple_instr(OP_ADD);
sp--;
break;
case op_minus:
assert(node->info.node.head != NULL);
if (node->info.node.head->next == NULL) {
emit_instr_immediate(OP_PUSH, 0);
sp++;
}
codegen(node->info.node.head->elem);
savedreal = real;
if (node->info.node.head->next != NULL)
codegen(node->info.node.head->next->elem);
if (savedreal != real)
compiler_error("Type mismatch");
if (real)
emit_simple_instr(FLOP_SUB);
else
emit_simple_instr(OP_SUB);
sp--;
break;
case op_times:
BINOP
if (real)
emit_simple_instr(FLOP_MUL);
else
emit_simple_instr(OP_MUL);
sp--;
break;
case op_divide:
BINOP
if (real)
emit_simple_instr(FLOP_DIV);
else
emit_simple_instr(OP_DIV);
sp--;
break;
case op_gt:
BINOP
if (real)
emit_simple_instr(FLOP_GT);
else
emit_simple_instr(OP_GT);
real = 0;
sp--;
break;
case op_lt:
BINOP
if (real)
emit_simple_instr(FLOP_LT);
else
emit_simple_instr(OP_LT);
real = 0;
sp--;
break;
case op_ge:
BINOP
if (real)
emit_simple_instr(FLOP_GE);
else
emit_simple_instr(OP_GE);
real = 0;
sp--;
break;
case op_le:
BINOP
if (real)
emit_simple_instr(FLOP_LE);
else
emit_simple_instr(OP_LE);
real = 0;
sp--;
break;
case op_eq:
BINOP
if (real)
emit_simple_instr(FLOP_EQ);
else
emit_simple_instr(OP_EQ);
real = 0;
sp--;
break;
case op_ne:
BINOP
if (real)
emit_simple_instr(FLOP_NE);
else
emit_simple_instr(OP_NE);
real = 0;
sp--;
break;
case op_and:
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next == NULL);
label = newlabel(NULL);
label2 = newlabel(NULL);
codegen(node->info.node.head->elem);
if (real)
compiler_error("Type mismatch");
emit_instr_label(OP_BZ, label);
sp--;
codegen(node->info.node.head->next->elem);
if (real)
compiler_error("Type mismatch");
emit_instr_label(OP_BZ, label);
emit_instr_immediate(OP_PUSH, 1);
emit_instr_label(OP_B, label2);
emit_label(label);
emit_instr_immediate(OP_PUSH, 0);
emit_label(label2);
break;
case op_or:
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next == NULL);
label = newlabel(NULL);
label2 = newlabel(NULL);
codegen(node->info.node.head->elem);
if (real)
compiler_error("Type mismatch");
emit_instr_label(OP_BNZ, label);
sp--;
codegen(node->info.node.head->next->elem);
if (real)
compiler_error("Type mismatch");
emit_instr_label(OP_BNZ, label);
emit_instr_immediate(OP_PUSH, 0);
emit_instr_label(OP_B, label2);
emit_label(label);
emit_instr_immediate(OP_PUSH, 1);
emit_label(label2);
break;
case stmt_if:
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
if (node->info.node.head->next->next)
assert(node->info.node.head->next->next->next
== NULL);
/* Evaluate condition first */
codegen(node->info.node.head->elem);
assert(real == 0);
label = newlabel(NULL); /* else */
label2 = newlabel(NULL); /* end of else */
emit_instr_label(OP_BZ, label);
sp--;
savedsp = sp;
/* Then evaluate the "if" code */
codegen(node->info.node.head->next->elem);
// printf("sp = %d, savedsp = %d\n", sp, savedsp);
assert(sp == savedsp);
if (node->info.node.head->next->next) {
emit_instr_label(OP_B, label2);
emit_label(label);
codegen(node->info.node.head->next->next->elem);
} else {
emit_label(label);
}
emit_label(label2);
/* Then, if we had an else, evaluate it here */
break;
case stmt_while:
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next == NULL);
/* Label at the start of the loop */
label = newlabel(NULL);
emit_label(label);
/* Evaluate the condition */
codegen(node->info.node.head->elem);
assert(real == 0);
/* Exit loop if condition evaluates to false */
label2 = newlabel(NULL);
savedbreaklabel = breaklabel;
breaklabel = label2;
emit_instr_label(OP_BZ, label2);
sp--;
/* Evaluate the loop interior */
codegen(node->info.node.head->next->elem);
/* Branch to the beginning of the loop */
emit_instr_label(OP_B, label);
/* Exit point */
emit_label(label2);
breaklabel = savedbreaklabel;
break;
case stmt_switch:
assert(node->info.node.head != NULL);
assert(node->info.node.head->next != NULL);
assert(node->info.node.head->next->next == NULL);
/* Verify constants, and calculate min and max */
savedswitchmin = switchmin;
savedswitchlabels = switchlabels;
savedswitchdefault = switchdefault;
savedbreaklabel = breaklabel;
switchmin = INT_MAX;
max = 0;
for (ptr =
node->info.node.head->next->elem->info.node.head;
ptr; ptr = ptr->next) {
num = 0;
switch (ptr->elem->tag) {
case casenum_ast:
num = ptr->elem->info.integer;
if (num > max)
max = num;
if (num < switchmin)
switchmin = num;
break;
case casevar_ast:
if (!lookup_constant(ptr->elem, &num))
compiler_error("Not a constant");
if (num > max)
max = num;
if (num < switchmin)
switchmin = num;
break;
default:
break;
}
}
if (switchmin > max)
switchmin = max;
range = max - switchmin;
if (range > 1024)
compiler_error("Excessive range in switch statement");
printf("Switch min %d max %d range %d\n", switchmin, max, range);
switchlabels = safe_malloc(sizeof(int)*(range+1));
switchdefault = newlabel(NULL);
for (i = 0; i <= range; i++)
switchlabels[i] = switchdefault;
/*
* Allocate labels for all used cases, and set
* the rest to default
*/
hasdefault = 0;
for (ptr =
node->info.node.head->next->elem->info.node.head;
ptr; ptr = ptr->next) {
switch (ptr->elem->tag) {
case casenum_ast:
num = ptr->elem->info.integer;
switchlabels[num-switchmin] =
newlabel(NULL);
break;
case casevar_ast:
lookup_constant(ptr->elem, &num);
switchlabels[num-switchmin] =
newlabel(NULL);
break;
case node_ast:
if (ptr->elem->info.node.tag ==
stmt_default)
hasdefault = 1;
default:
break;
}
}
switchend = newlabel(NULL);
stackfixlabel = newlabel(NULL);
breaklabel = switchend;
/* Evaluate condition */
codegen(node->info.node.head->elem);
if (switchmin < 0) {
emit_instr_immediate(OP_PUSH, -switchmin);
emit_simple_instr(OP_ADD);
}
if (switchmin > 0) {
emit_instr_immediate(OP_PUSH, switchmin);
emit_simple_instr(OP_SUB);
}
/* Range check */
emit_instr_immediate(OP_LOAD, 0);
emit_instr_immediate(OP_PUSH, 0);
emit_simple_instr(OP_LT);
emit_instr_label(OP_BNZ, stackfixlabel);
emit_instr_immediate(OP_LOAD, 0);
emit_instr_immediate(OP_PUSH, range);
emit_simple_instr(OP_GT);
emit_instr_label(OP_BNZ, stackfixlabel);
/* Emit branch table */
emit_instr_immediate(OP_PUSH, 3);
emit_simple_instr(OP_MUL);
emit_simple_instr(OP_BI);
for (i = 0; i <= range; i++)
/*
* We must constrain branch table entries to
* a fixed size
*/
emit_instr_label_sized(OP_B, switchlabels[i],
3);
emit_label(stackfixlabel);
emit_instr_immediate(OP_POP, 1);
emit_instr_label(OP_B, switchdefault);
sp--;
printf("sp = %d\n", sp);
/* Evaluate all cases */
codegen(node->info.node.head->next->elem);
printf("sp = %d\n", sp);
if (!hasdefault)
emit_label(switchdefault);
emit_label(switchend);
free(switchlabels);
switchmin = savedswitchmin;
switchlabels = savedswitchlabels;
switchdefault = savedswitchdefault;
breaklabel = savedbreaklabel;
break;
case stmt_default:
emit_label(switchdefault);
break;
case stmt_break:
emit_instr_label(OP_B, breaklabel);
break;
case stmt_return:
assert(node->info.node.head == NULL);
emit_instr_imm_const(OP_POP, sp, fnconst);
emit_simple_instr(OP_RET);
break;
default:
printf("unknown list type:\n");
break;
}
break;
default:
printf("unknown node type\n");
break;
}
}
void output_byte(int byte)
{
#if MOREDEBUG
printf("Output byte: %x\n", byte);
#endif
fputc(byte, binout);
}
void output_int(int num)
{
#if MOREDEBUG
printf("Output int: %x\n", num);
#endif
output_byte((num >> 24) & 0xff);
output_byte((num >> 16) & 0xff);
output_byte((num >> 8) & 0xff);
output_byte(num & 0xff);
}
void output_string(char *string)
{
#if MOREDEBUG
printf("Output string: \"%s\"\n", string);
#endif
fputs(string, binout);
}
int bytesmask[4] = { 0xff, 0xffff, 0xffffff, 0xffffffff };
#define BYTESMASK(bytes) (bytesmask[bytes])
void output_code(void)
{
struct instr *ptr;
int morepasses = 1;
int passno = 0;
int pc;
int abiver;
while (morepasses) {
ptr = instr_head;
pc = 0;
morepasses = 0;
#if DEBUG
printf("Starting pass %d\n", passno);
#endif
while (ptr) {
if (ptr->flags & IF_ISLABEL)
if (get_label(ptr->label) != pc) {
set_label(ptr->label, pc);
#if DEBUG
printf("New pass (label) at PC = %d\n", pc);
#endif
morepasses = 1;
}
if (ptr->flags & IF_ISINSTR)
pc += ptr->bytes;
if ((passno > 0) && (ptr->flags & IF_HASOPERAND)) {
int operand;
operand = ptr->operand;
if (ptr->flags & IF_ADDCONST)
operand += get_const(ptr->constant);
if (ptr->flags & IF_ADDLABEL)
operand += get_label(ptr->label);
if (ptr->flags & IF_SUBPC)
operand -= pc;
assert(ptr->bytes <= 5);
#if MOREDEBUG
printf("operand = %x, bytes = %d, mask = %x\n", operand, ptr->bytes-2, BYTESMASK(ptr->bytes-2));
#endif
while ((operand & BYTESMASK(ptr->bytes-2)) !=
operand) {
if ((!morepasses) &&
(ptr->flags & IF_FIXEDSIZE)) {
fprintf(stderr, "Label out of range: lab_%d: %d\n", ptr->label, operand);
compiler_error("Label out of range");
}
if (!(ptr->flags & IF_FIXEDSIZE)) {
ptr->bytes++;
pc++;
morepasses = 1;
#if DEBUG
printf("New pass at PC = %d\n", pc);
#endif
} else {
break;
}
assert(ptr->bytes <= 5);
}
}
ptr = ptr->next;
}
passno++;
#if DEBUG
printf("End of pass pc = %d\n", pc);
#endif
}
#if DEBUG
printf("Finished passes.. outputting code\n");
#endif
/*
* Code is relocatable. Output function table, and fix up
* pointers by the size of the header.
*/
output_byte(MAGIC1);
output_byte(MAGIC2);
output_byte(VERSION1);
output_byte(VERSION2);
#define ABIVERSION(x) do { \
abiver = 0; \
if (!lookup_constant_string(x, &abiver)) \
printf("%s not defined\n", x); \
output_int(abiver); \
printf("ABIVERSION(%s) = %x\n", x, abiver); \
} while (0)
pc = (pc + 3) & ~3; /* Align table */
output_int(pc+28); /* Pointer to function table */
ABIVERSION("__abiversion1");
ABIVERSION("__abiversion2");
ABIVERSION("__abiversion3");
ABIVERSION("__abiversion4");
ABIVERSION("__abiversion5");
ptr = instr_head;
pc = 0;
while (ptr) {
if (ptr->flags & IF_ISINSTR) {
int bytes;
bytes = ptr->bytes;
pc+=ptr->bytes;
bytes--;
if (ptr->flags & IF_HASOPERAND) {
int operand;
#if MOREDEBUG
printf("bytes = %d\n", bytes);
#endif
output_byte(ptr->opcode | SET_BYTECOUNT(bytes));
operand = ptr->operand;
if (ptr->flags & IF_ADDCONST)
operand += get_const(ptr->constant);
if (ptr->flags & IF_ADDLABEL)
operand += get_label(ptr->label);
if (ptr->flags & IF_SUBPC)
operand -= pc;
while (bytes) {
output_byte((operand >> (8*(bytes-1)))
& 0xff);
bytes--;
}
} else if (ptr->flags & IF_HASSTRING) {
int len;
len = strlen(ptr->string);
bytes -= len;
output_byte(ptr->opcode | SET_BYTECOUNT(bytes));
while (bytes) {
output_byte((len >> (8*(bytes-1)))
& 0xff);
bytes--;
}
output_string(ptr->string);
} else {
output_byte(ptr->opcode);
}
}
ptr = ptr->next;
}
while (pc % 4) {
output_byte(0);
pc++;
}
output_functions();
}