/*
** $Id: build.c,v 1.12 2003/06/09 10:18:52 dredd Exp $
**
** $Source: /cvsroot/swlpc/swlpc/lpc/build.c,v $
** $Revision: 1.12 $
** $Date: 2003/06/09 10:18:52 $
** $State: Exp $
**
** Author: Geoff Wong
** Copyright(C) 1993-1998
** geoff@serc.rmit.edu.au
**
** See the file "Copying" distributed with this file.
*/
/*
* Program building stuff.
*
* Like all the detailed comments? (not)
*
* The emit_* instructions emit a variety of different
* instruction formats into a byte array.
*
* The patch functions build and process a linked list of
* stuff that needs to be checked on the second parse (ie. jumps etc).
*
* The lex string stuff is a different format string designed for
* easy addition at compile time (to avoid strcpy n^2 problems).
*/
#include <stdlib.h>
#include <assert.h>
#include "lpc.h"
#include "lexer.h"
#include "build.h"
#include "apply.h"
// #define DEBUG
extern int global_offset;
extern Func *CPD;
extern struct var_def
* arg_head,
* local_head;
extern int
num_args,
max_locals,
num_locals;
extern union
{
float r;
int i;
lexar *s;
}
ultima, penultima;
Patch * break_head; /* list of patches for break statements */
Patch * add_patch(Func * f, int tipe, void * v);
static unsigned int no_progs = 0,
total_prog_size = 0;
static int Zero = 0;
int uinstr = 0,
puinstr = 0;
/* returns new block pointer and fixes size */
/* yuk :-) */
byte * code_size_fix(Func * p)
{
byte *t, *t1;
int size;
t = p->block;
size = p->current_I * sizeof(byte);
t1 = (byte *) malloc(size + sizeof(char *));
bcopy(t, t1, p->current_I * sizeof(byte));
free(t);
#if 0
printf("Function %s : type %d\n", p->name, (int) p->type);
#endif
no_progs++;
total_prog_size += (int) (size) + sizeof(Func) +
(p->num_str * sizeof(char *));
p->size = size;
return t1;
}
void emits(byte instr)
{
#if 0
printf("E: %s\n", codes[find_ocode(instr)].name);
#endif
if (CPD->current_I >= CPD->size - 1)
{
CPD->size = CPD->size * 2;
CPD->block = (byte *) realloc(CPD->block, CPD->size);
}
CPD->block[CPD->current_I++] = instr;
puinstr = uinstr;
uinstr = (int) instr;
}
void emit(byte instr, byte arg)
{
#if 0
printf("E: %s, %c\n", codes[find_ocode(instr)].name, arg);
#endif
if (CPD->current_I >= CPD->size - 2)
{
CPD->size = CPD->size * 2;
CPD->block = (byte *) realloc(CPD->block, CPD->size);
}
CPD->block[CPD->current_I++] = instr;
CPD->block[CPD->current_I++] = arg;
puinstr = uinstr;
uinstr = (int) instr;
}
void emit16(byte instr, short parg)
{
unsigned char a, b;
char *arg = (char *) &parg;
if (CPD->current_I >= CPD->size - 3)
{
CPD->size = CPD->size * 2;
CPD->block = (byte *) realloc(CPD->block, CPD->size);
}
#ifdef ENDIAN
a = (byte) arg[1];
b = (byte) arg[0];
#else
a = (byte) arg[0];
b = (byte) arg[1];
#endif
CPD->block[CPD->current_I++] = instr;
CPD->block[CPD->current_I++] = a;
CPD->block[CPD->current_I++] = b;
puinstr = uinstr;
uinstr = (int) instr;
}
void emit32(byte instr, char *arg)
{
byte a, b, c, d;
if (CPD->current_I >= CPD->size - 5)
{
CPD->size = CPD->size * 2;
CPD->block = (byte *) realloc(CPD->block, CPD->size);
}
#ifdef ENDIAN
a = (byte) arg[3];
b = (byte) arg[2];
c = (byte) arg[1];
d = (byte) arg[0];
#else
a = (byte) arg[0];
b = (byte) arg[1];
c = (byte) arg[2];
d = (byte) arg[3];
#endif
CPD->block[CPD->current_I++] = instr;
CPD->block[CPD->current_I++] = a;
CPD->block[CPD->current_I++] = b;
CPD->block[CPD->current_I++] = c;
CPD->block[CPD->current_I++] = d;
puinstr = uinstr;
uinstr = (int) instr;
}
void emit64(byte instr, char *arg)
{
byte a, b, c, d, e, f, g, h;
if (CPD->current_I >= CPD->size - 9)
{
CPD->size = CPD->size * 2;
CPD->block = (byte *) realloc(CPD->block, CPD->size);
}
#ifdef ENDIAN
a = (byte) arg[7];
b = (byte) arg[6];
c = (byte) arg[5];
d = (byte) arg[4];
e = (byte) arg[3];
f = (byte) arg[2];
g = (byte) arg[1];
h = (byte) arg[0];
#else
a = (byte) arg[0];
b = (byte) arg[1];
c = (byte) arg[2];
d = (byte) arg[3];
e = (byte) arg[4];
f = (byte) arg[5];
g = (byte) arg[6];
h = (byte) arg[7];
#endif
CPD->block[CPD->current_I++] = instr;
CPD->block[CPD->current_I++] = a;
CPD->block[CPD->current_I++] = b;
CPD->block[CPD->current_I++] = c;
CPD->block[CPD->current_I++] = d;
CPD->block[CPD->current_I++] = e;
CPD->block[CPD->current_I++] = f;
CPD->block[CPD->current_I++] = g;
CPD->block[CPD->current_I++] = h;
puinstr = uinstr;
uinstr = (int) instr;
}
int emit_number(int num)
{
if (num <= MAXFIXEDINT && num >= MINFIXEDINT)
{
emit(S_PUSH8, num);
}
else
{
emit32(S_PUSHi, (char *) &num);
}
return 0;
}
#if 0
int emit_cnumber(int num)
{
if (num < 256)
{
emit(S_PUSHC, num);
}
else
{
emit32(S_PUSHCi, (char *) &num);
}
return 0;
}
#endif
void emit_push(int var_type, int s)
{
if (var_type == LOCAL)
{
emit(S_PUSHL, s);
return;
}
else if (var_type == GLOBAL)
{
emit(S_PUSHG, s - 1);
return;
}
else if (var_type == INDEX_TY)
{
/* emits(S_PUSHI, NONE); */
return;
}
else
return;
yyerrorf("Unknown variable \"%s\" used (push).\n", s);
return;
}
/*
* Change a constant into its value in the code.
*/
void emit_constant(Val * v)
{
lexar *l;
switch (v->type)
{
case T_NUMBER:
penultima.i = ultima.i;
ultima.i = v->u.number;
emit_number(v->u.number);
break;
case T_REAL:
emit32(S_PUSHr, (char *) &v->u.real);
break;
case T_STRING:
l = lex_str_copy(v->u.string->str);
penultima.s = ultima.s;
ultima.s = l;
add_patch(CPD, P_STRING, l);
emit64(S_PUSHs, (char *)&Zero);
break;
default:
yyerrorf("Illegal constant usage (somehow).\n");
}
}
void emit_pop(int s)
{
emits(S_POPI);
return;
#if 0
if (var_type == LOCAL)
{
emit(S_POPL, s);
return;
}
else if (var_type == GLOBAL)
{
emit(S_POPG, s - 1);
return;
}
else if (var_type == INDEX_TY)
{
emits(S_POPI);
return;
}
else
return;
yyerrorf("Unknown variable \"%s\" used (pop).\n", s);
#endif
}
void patch8(int loc, byte val)
{
CPD->block[loc] = val;
}
void patch16(int loc, short pval)
{
byte a, b;
char *val = (char *) &pval;
#if 0
printf("patch16 : %d\n", (int) val);
#endif
#ifdef ENDIAN
a = (byte) val[1];
b = (byte) val[0];
#else
a = (byte) val[0];
b = (byte) val[1];
#endif
CPD->block[loc] = a;
CPD->block[loc + 1] = b;
}
/*
* Hmm - switch statement support.
*/
unsigned int switch_no = 0;
Shared * make_next_switch()
{
Shared * ret;
char *s = malloc(strlen("*switch") + 10); /* 9 is many... */
sprintf(s, "*switch%d", switch_no++);
ret = string_copy(s);
free(s);
return ret;
}
/*
* break patch stuff
*/
Patch * add_break_patch(int offset)
{
Patch *tmp;
tmp = (Patch *) malloc(sizeof(Patch));
tmp->value = 0;
tmp->offset = offset;
tmp->tipe = P_BREAK;
tmp->next = break_head;
break_head = tmp;
return tmp;
}
void do_break_patch(int backto, int line)
{
Patch *tmp;
while (break_head)
{
if (break_head->offset <= backto) break;
/* ok - do the patch back */
if (CPD->block[break_head->offset] != S_JUMPU)
{
fatal("Compile error in do_break_patch().\n");
return;
}
CPD->block[break_head->offset] = S_JUMP;
patch16(break_head->offset + 1,
line - ((short) break_head->offset + 3));
tmp = break_head;
break_head = break_head->next;
free(tmp);
}
}
extern int prog_arg;
Val * share_Cstring(Shared * str)
{
Val *ret = alloc_value();
ret->type = T_STRING;
ret->u.string = shared_string_copy(str);
return ret;
}
CVal * make_const(Shared * s, Val * v, int t)
{
CVal *ret = (CVal *) malloc(sizeof(CVal));
ret->type = t;
ret->val = v;
ret->name = shared_string_copy(s);
return ret;
}
/*
* Strings in lnode space are stored straight - no ref counts etc - that is
* because they are never freed.
*/
#define INITIAL_FUN_LEN 4
extern int current_line;
Func * alloc_function(int type, Shared * name, int size, int num_var)
{
/* deliberately ignoring num_var nowdays.. */
Func *p;
p = (Func *) malloc(sizeof(Func));
p->type = (short) type;
p->line = current_line;
//p->name = shared_string_copy(name);
p->name = name; /* should have been allocd by lexer */
p->num_var = 0;
p->num_str = 0;
p->block = (byte *) malloc(size * sizeof(byte));
p->linemap = (unsigned short *) malloc(INITIAL_FUN_LEN * sizeof(short));
memset(p->linemap, 0, INITIAL_FUN_LEN * sizeof(short));
p->num_lines = INITIAL_FUN_LEN;
p->size = size;
p->next = 0;
p->num_arg = 0;
p->current_I = 0;
#ifdef COUNT_PROFILE
p->calls = 0;
#endif
p->patch = NULL;
return p;
}
void new_function()
{
arg_head = 0; /* local args */
num_args = 0;
local_head = 0; /* local variables */
max_locals = 0;
num_locals = 0;
break_head = 0; /* break stuff */
}
/*
* string and function call patch stuff
*/
Patch * add_patch(Func * f, int tipe, void * v)
{
Patch *tmp;
#if 0
printf("adding to %s patch type: %d\n", f->name, tipe);
#endif
tmp = (Patch *) malloc(sizeof(Patch));
tmp->value = v;
tmp->offset = f->current_I;
tmp->tipe = tipe;
tmp->next = (Patch *) f->patch;
f->patch = (Shared **) tmp;
if (tipe == P_STRING || tipe == P_CSTRING)
f->num_str++;
return tmp;
}
void remove_patch(Func * f)
{
Patch *old;
if (f == NULL) return;
old = (Patch *) f->patch;
if (old == NULL) return;
f->patch = (Shared **) old->next;
old->next = 0;
if (old->tipe == P_STRING || old->tipe == P_CSTRING)
f->num_str--;
/* free(tmp->value)?? -- depends on the tipe?? */
free(old);
}
/* returns a list of strings (should be freed on prog destruction) */
/* returns 0 on failure */
int process_patches(Class * O)
{
// Obj *OldCO = Scurrent();
Func *func = O->prog, *y;
Patch *tmp, *old;
byte a, b, c, d, e, f ,g ,h;
int count = 0, diff, off, prev, err_flag = 1;
char * yy, warn[1024], * tfunname, * tmpf;
Shared * x = NULL;
Shared * funname, * inhname;
Shared ** slist;
#if 0
FIX: Sset_current(O, "process_patches");
printf("Processing patches for %s\n", O->name);
#endif
while (func)
{
count = 0;
#if 0
printf("func: %s\n", f->name);
#endif
if (func->num_str)
slist = (Shared **) malloc(sizeof(Shared *) * (func->num_str));
else
slist = 0;
/* if (f->type & TY_UNDEFC) { */
tmp = (Patch *) func->patch;
while (tmp)
{
off = (int) tmp->offset;
switch (tmp->tipe)
{
case P_CSTRING:
/* patch Shared strings - put them in list */
/* to be freed with program */
slist[count++] = x;
break;
case P_STRING:
/* patch lexar strings */
x = to_Cstring((lexar *) tmp->value);
#if 0
printf("P_STRING string %s\n", x->str);
#endif
yy = (char *) &x;
#ifdef ENDIAN
a = (byte) yy[7];
b = (byte) yy[6];
c = (byte) yy[5];
d = (byte) yy[4];
e = (byte) yy[3];
f = (byte) yy[2];
g = (byte) yy[1];
h = (byte) yy[0];
#else
a = (byte) yy[0];
b = (byte) yy[1];
c = (byte) yy[2];
d = (byte) yy[3];
e = (byte) yy[4];
f = (byte) yy[5];
g = (byte) yy[6];
h = (byte) yy[7];
#endif
func->block[off + 1] = a;
func->block[off + 2] = b;
func->block[off + 3] = c;
func->block[off + 4] = d;
func->block[off + 5] = e;
func->block[off + 6] = f;
func->block[off + 7] = g;
func->block[off + 8] = h;
/* build list .. */
// free the lexar?
free(tmp->value);
slist[count++] = x;
break;
case P_CALLU:
/* FIX: */
prev = 0;
tfunname = unshared_str_copy((Shared *)tmp->value);
#ifdef DEBUG
printf("P_CALLU string %s (%s)\n", ((Shared *)tmp->value)->str, tfunname);
#endif
if ((tmpf = strchr(tfunname, ':')) && (tmpf[1] == ':'))
{
if (tfunname[0] != ':')
{
tmpf[0] = '\0';
inhname = string_copy(tfunname);
// printf(" inhname=%s\n", inhname->str);
}
else
{
inhname = NULL;
}
funname = string_copy(&(tmpf[2]));
prev = 1;
}
else
{
funname = shared_string_copy(tmp->value);
inhname = NULL;
}
/* FIX */
y = find_program(funname, O, O, inhname, prev);
/* should we free tmp->value?? */
if (!y)
{
sprintf(warn, "local function \"%s\" not found.",
((Shared *)tmp->value)->str);
#if 1
/* what's O? */
printf("Local function \"%s\" not found in %s.\n",
((Shared *)tmp->value)->str, O->name->str);
#endif
/* force an error in load_object! */
err_flag = 0;
yyerrord(warn, func->line);
prog_arg = 0;
}
else
{
prog_arg = y->num_arg;
#ifdef DEBUG
printf(" found function %s:%d\n", y->name->str, prog_arg);
#endif
}
free(tfunname);
free_string(funname);
/* might fuckup with consts strings ? */
if (prev && tmp->value) free_string(tmp->value);
if (inhname != NULL) free_string(inhname);
/* this all replaces an unknown call with */
/* a fixed location local call */
//emit(S_PUSHCARGS, $3);
//emit64(S_PUSHCRA, zero);
//emit(S_ADDSP, 0); /* varargs fix - blech */
diff = prog_arg - func->block[off - 5];
// this code is brittle as fuck - it search back for the specific instructions to fix ..
func->block[off - 1] = diff; /* to be added to rSP */
func->block[off - 5] = prog_arg; /* # args passed */
assert(global_offset < 256);
func->block[off - 3] = global_offset; /* global offset */
func->block[off] = S_CALL; /* set CALL */
yy = (char *) &y;
#ifdef ENDIAN
a = (byte) yy[7];
b = (byte) yy[6];
c = (byte) yy[5];
d = (byte) yy[4];
e = (byte) yy[3];
f = (byte) yy[2];
g = (byte) yy[1];
h = (byte) yy[0];
#else
a = (byte) yy[0];
b = (byte) yy[1];
c = (byte) yy[2];
d = (byte) yy[3];
e = (byte) yy[4];
f = (byte) yy[5];
g = (byte) yy[6];
h = (byte) yy[7];
#endif
func->block[off + 1] = a; /* set location */
func->block[off + 2] = b;
func->block[off + 3] = c;
func->block[off + 4] = d;
func->block[off + 5] = e;
func->block[off + 6] = f;
func->block[off + 7] = g;
func->block[off + 8] = h;
break;
default:
#if 0
Sset_current(OldCO, "process_patches 2");
printf("Fuck up in process_patch on %s in %s.\n", f->name, O->name);
#endif
fatal("Fuck up in process patch.\n");
return 0;
}
old = tmp;
tmp = tmp->next;
free(old);
}
/* slist[count] = 0; */
func->patch = slist;
/* f->type = f->type ^ TY_UNDEFC; } */
func = func->next;
}
// Sset_current(OldCO, "process_patches 3");
return err_flag;
}
/*
* free_prog now kills a program in one hit, as they are allocated in a
* single block.
* -- well - actually only functions are linear - :-) Dredd
*/
int free_prog(Func * p)
{
Func *t = p, *l;
int i;
while (t)
{
no_progs--;
total_prog_size -= ((int) (t->size) + sizeof(Func *) +
(t->num_str * sizeof(char *)));
free(t->block);
free(t->linemap);
free_string(t->name); /* prob? */
for (i = 0; i < t->num_str; i++)
free_string(t->patch[i]);
t->num_str = 0;
if (t->patch) free(t->patch); /* free the patch space? */
t->patch = NULL;
l = t->next;
free(t);
t = l;
}
return 1;
}
int free_bad_prog(Func * p)
{
Func *t = p, *l;
Patch *pt, *ot;
while (t)
{
no_progs--;
total_prog_size -= t->size;
free(t->block);
free_string(t->name); /* prob? */
pt = (Patch *) t->patch;
while (pt)
{
ot = pt;
pt = pt->next;
free(ot);
}
l = t->next;
free(t);
t = l;
}
return 1;
}
void free_global_tables(Class * code)
{
int i;
int num_globs;
int inherited_globs = 0;
if (code->inherit)
{
for (i = 0; code->inherit[i]; i++)
{
inherited_globs += code->inherit[i]->num_variables;
}
}
for (i = inherited_globs; i < code->num_variables; i++)
{
free_string(code->global_table[i]->u.string);
free(code->global_table[i]);
}
if (code->global_table)
{
free(code->global_table);
code->global_table = 0;
}
for (i = 0; i < code->num_constants; i++)
{
// CHECK:
free(code->constant_table[i]->val);
free_string(code->constant_table[i]->name);
free(code->constant_table[i]);
}
if (code->constant_table)
{
free(code->constant_table);
code->constant_table = 0;
}
}
int show_prog_status()
{
add_message("No of functions %d, total size = %d, av size = %d\n",
no_progs, total_prog_size, total_prog_size / no_progs);
return total_prog_size;
}
#if 0
/*
* Given a program counter, find the enclosing function.
* Returns null if the function isn't in the code segment for
* the given object. Doesn't look at inherited funcs.
*/
Func * find_local_header(Obj * ob, byte * pc)
{
Func *f = ob->prog;
if (!pc)
return 0;
while (f)
{
if ((pc >= f->block) && (pc < (f->block + f->size)))
return f;
f = f->next;
}
return 0;
}
#endif