Below is the file 'src/lsc/codegen.c' from this revision. You can also download the file.
/* code.c */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <err.h> #include <limits.h> #include <stdarg.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 #define ERROR_MAXLEN 1024 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_TYPE_BASE 0xff #define FN_INT 1 #define FN_STR 2 #define FN_LOCAL 3 #define FN_VARARGS 0x100 int fnconst; int nargs; int local_variable_count = -1; int local_arg_count = -1; int function_count = -1; int constant_count = -1; int lineno; #define HASHSIZE 512 #define HASHMASK (HASHSIZE - 1) struct hashentry *varhash[HASHSIZE]; struct hashentry *fnhash[HASHSIZE]; struct hashentry *constanthash[HASHSIZE]; void compiler_error(char *str, ...) { char buf[ERROR_MAXLEN]; va_list args; snprintf(buf, ERROR_MAXLEN, "%d: %s", lineno, str); va_start(args, str); verrx(1, buf, args); va_end(args); /* Not really necessary if errx exits */ } 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_TYPE_BASE) == 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 & FN_TYPE_BASE) { 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; int savedlineno; union { int i; float f; } conv; savedlineno = lineno; lineno = node->lineno; #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; } compiler_error("variable '%s' used before assignment", var.name); } // 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: case kind_fndefint_v: 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.tag == kind_fndefint_v) ? FN_VARARGS : 0), 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); nargs = 0; /* Evaluate the arguments first */ for (ptr = node->info.node.head->next->elem->info.node.head; ptr; ptr = ptr->next) { codegen(ptr->elem); nargs++; } // printf("sp = %d\n", sp); if (!lookup_function(node->info.node.head->elem, &fn)) compiler_error("Function not found"); if (fn.type & FN_VARARGS) { /* * The last argument is the number of * arguments to expect in the case * of variable argument length. * This is something only supported for * builtin functions at present. */ emit_instr_immediate(OP_PUSH, nargs); sp++; } emit_instr_immediate(OP_ALLOC, 1); sp++; 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: if (node->info.node.head) { /* Return value */ codegen(node->info.node.head->elem); emit_instr_imm_const(OP_STORE, sp, fnconst); sp--; } 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(); }