1276 lines
37 KiB
C
1276 lines
37 KiB
C
/*
|
|
* code generator for runtime LPC code
|
|
*/
|
|
#include "std.h"
|
|
#include "lpc_incl.h"
|
|
#include "icode.h"
|
|
#include "compiler.h"
|
|
#include "generate.h"
|
|
|
|
/*
|
|
* Macro for inserting global variable indices.
|
|
*/
|
|
|
|
#if CFG_MAX_GLOBAL_VARIABLES <= 256
|
|
#define INS_GLOBAL_INDEX ins_byte
|
|
#elif CFG_MAX_GLOBAL_VARIABLES <= 65536
|
|
#define INS_GLOBAL_INDEX ins_short
|
|
#else
|
|
#error CFG_MAX_GLOBAL_VARIABLES must not be greater than 65536
|
|
#endif
|
|
|
|
static void ins_real (double);
|
|
static void ins_short (short);
|
|
static void upd_short (int, int, const char *);
|
|
static void ins_byte (unsigned char);
|
|
static void upd_byte (int, unsigned char);
|
|
static void write_number (long);
|
|
static void ins_int (long);
|
|
#if SIZEOF_PTR == 8
|
|
static void ins_long (long);
|
|
#endif
|
|
void i_generate_node (parse_node_t *);
|
|
static void i_generate_if_branch (parse_node_t *, int);
|
|
static void i_generate_loop (int, parse_node_t *, parse_node_t *,
|
|
parse_node_t *);
|
|
static void i_update_branch_list (parse_node_t *, const char *);
|
|
static int try_to_push (int, int);
|
|
|
|
static int foreach_depth = 0;
|
|
|
|
static int current_num_values;
|
|
|
|
static unsigned int last_size_generated;
|
|
static unsigned int line_being_generated;
|
|
|
|
static int push_state;
|
|
static int push_start;
|
|
|
|
static parse_node_t *branch_list[3];
|
|
static int nforward_branches, nforward_branches_max;
|
|
static int *forward_branches = 0;
|
|
|
|
static void ins_real (double l)
|
|
{
|
|
float f = (float)l;
|
|
|
|
if (prog_code + 4 > prog_code_max) {
|
|
mem_block_t *mbp = &mem_block[A_PROGRAM];
|
|
|
|
UPDATE_PROGRAM_SIZE;
|
|
realloc_mem_block(mbp);
|
|
|
|
prog_code = mbp->block + mbp->current_size;
|
|
prog_code_max = mbp->block + mbp->max_size;
|
|
}
|
|
STORE_FLOAT(prog_code, f);
|
|
}
|
|
|
|
/*
|
|
* Store a 2 byte number. It is stored in such a way as to be sure
|
|
* that correct byte order is used, regardless of machine architecture.
|
|
* Also beware that some machines can't write a word to odd addresses.
|
|
*/
|
|
static void ins_short (short l)
|
|
{
|
|
if (prog_code + 2 > prog_code_max) {
|
|
mem_block_t *mbp = &mem_block[A_PROGRAM];
|
|
UPDATE_PROGRAM_SIZE;
|
|
realloc_mem_block(mbp);
|
|
|
|
prog_code = mbp->block + mbp->current_size;
|
|
prog_code_max = mbp->block + mbp->max_size;
|
|
}
|
|
STORE_SHORT(prog_code, l);
|
|
}
|
|
|
|
/*
|
|
* Store a 4 byte number. It is stored in such a way as to be sure
|
|
* that correct byte order is used, regardless of machine architecture.
|
|
*/
|
|
static void ins_int (long l)
|
|
{
|
|
|
|
if (prog_code + SIZEOF_LONG > prog_code_max) {
|
|
mem_block_t *mbp = &mem_block[A_PROGRAM];
|
|
UPDATE_PROGRAM_SIZE;
|
|
realloc_mem_block(mbp);
|
|
|
|
prog_code = mbp->block + mbp->current_size;
|
|
prog_code_max = mbp->block + mbp->max_size;
|
|
}
|
|
STORE_INT(prog_code, l);
|
|
}
|
|
|
|
/*
|
|
* Store a 8 byte number. It is stored in such a way as to be sure
|
|
* that correct byte order is used, regardless of machine architecture.
|
|
*/
|
|
#if SIZEOF_PTR == 8
|
|
static void ins_long (long l)
|
|
{
|
|
if (prog_code + 8 > prog_code_max) {
|
|
mem_block_t *mbp = &mem_block[A_PROGRAM];
|
|
UPDATE_PROGRAM_SIZE;
|
|
realloc_mem_block(mbp);
|
|
|
|
prog_code = mbp->block + mbp->current_size;
|
|
prog_code_max = mbp->block + mbp->max_size;
|
|
}
|
|
STORE_PTR(prog_code, l);
|
|
}
|
|
#endif
|
|
|
|
static void upd_short (int offset, int l, const char * where)
|
|
{
|
|
unsigned short s;
|
|
|
|
IF_DEBUG(UPDATE_PROGRAM_SIZE);
|
|
DEBUG_CHECK2(offset > CURRENT_PROGRAM_SIZE,
|
|
"patch offset %x larger than current program size %x.\n",
|
|
offset, CURRENT_PROGRAM_SIZE);
|
|
if (l > USHRT_MAX) {
|
|
char buf[256];
|
|
|
|
sprintf(buf, "branch limit exceeded in %s, near line %i",
|
|
where, line_being_generated);
|
|
yyerror(buf);
|
|
}
|
|
s = l;
|
|
|
|
COPY_SHORT(mem_block[A_PROGRAM].block + offset, &s);
|
|
}
|
|
|
|
static void ins_rel_short (int l)
|
|
{
|
|
if (l > USHRT_MAX) {
|
|
char buf[256];
|
|
|
|
sprintf(buf, "branch limit exceeded in switch table, near line %i",
|
|
line_being_generated);
|
|
yyerror(buf);
|
|
}
|
|
ins_short(l);
|
|
}
|
|
|
|
static void ins_byte (unsigned char b)
|
|
{
|
|
if (prog_code == prog_code_max) {
|
|
mem_block_t *mbp = &mem_block[A_PROGRAM];
|
|
UPDATE_PROGRAM_SIZE;
|
|
realloc_mem_block(mbp);
|
|
|
|
prog_code = mbp->block + mbp->current_size;
|
|
prog_code_max = mbp->block + mbp->max_size;
|
|
}
|
|
*prog_code++ = b;
|
|
}
|
|
|
|
static void upd_byte (int offset, unsigned char b)
|
|
{
|
|
IF_DEBUG(UPDATE_PROGRAM_SIZE);
|
|
DEBUG_CHECK2(offset > CURRENT_PROGRAM_SIZE,
|
|
"patch offset %x larger than current program size %x.\n",
|
|
offset, CURRENT_PROGRAM_SIZE);
|
|
mem_block[A_PROGRAM].block[offset] = b;
|
|
}
|
|
|
|
static void end_pushes (void) {
|
|
if (push_state) {
|
|
if (push_state > 1)
|
|
upd_byte(push_start, push_state);
|
|
push_state = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// F_PUSH pushes multiple data items onto the stack. This function
|
|
// sets up the multi push by reading the single push off the top of
|
|
// the stack and rewriting the mult-push into the stack.
|
|
//
|
|
|
|
static void initialize_push (void) {
|
|
int what = mem_block[A_PROGRAM].block[push_start];
|
|
int arg = 0;
|
|
|
|
// This is only valid if it is not a one byte const target.
|
|
if (what != F_CONST0 && what != F_CONST1) {
|
|
arg = mem_block[A_PROGRAM].block[push_start + 1];
|
|
}
|
|
prog_code = mem_block[A_PROGRAM].block + push_start;
|
|
ins_byte(F_PUSH);
|
|
push_start++; /* now points to the zero here */
|
|
ins_byte(0);
|
|
|
|
switch (what) {
|
|
case F_CONST0:
|
|
ins_byte(PUSH_NUMBER | 0);
|
|
break;
|
|
case F_CONST1:
|
|
ins_byte(PUSH_NUMBER | 1);
|
|
break;
|
|
case F_BYTE:
|
|
ins_byte(PUSH_NUMBER | arg);
|
|
break;
|
|
case F_SHORT_STRING:
|
|
ins_byte(PUSH_STRING | arg);
|
|
break;
|
|
case F_LOCAL:
|
|
ins_byte(PUSH_LOCAL | arg);
|
|
break;
|
|
case F_GLOBAL:
|
|
ins_byte(PUSH_GLOBAL | arg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate the code to push a number on the stack.
|
|
* This varies since there are several opcodes (for
|
|
* optimizing speed and/or size).
|
|
*/
|
|
static void write_small_number (int val) {
|
|
if (try_to_push(PUSH_NUMBER, val)) return;
|
|
ins_byte(F_BYTE);
|
|
ins_byte(val);
|
|
}
|
|
|
|
static void write_number (long val)
|
|
{
|
|
if ((val & ~0xff) == 0)
|
|
write_small_number(val);
|
|
else {
|
|
end_pushes();
|
|
if (val < 0 && val > -256) {
|
|
ins_byte(F_NBYTE);
|
|
ins_byte(-val);
|
|
} else if (val >= -32768 && val <= 32767) {
|
|
ins_byte(F_SHORT_INT);
|
|
ins_short(val);
|
|
} else {
|
|
ins_byte(F_NUMBER);
|
|
#if SIZEOF_LONG == 4
|
|
ins_int(val);
|
|
#else
|
|
ins_long(val);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
generate_expr_list (parse_node_t * expr) {
|
|
parse_node_t *pn;
|
|
int n, flag;
|
|
|
|
if (!expr) return;
|
|
pn = expr;
|
|
flag = n = 0;
|
|
do {
|
|
if (pn->type & 1) flag = 1;
|
|
i_generate_node(pn->v.expr);
|
|
n++;
|
|
} while ((pn = pn->r.expr));
|
|
|
|
if (flag) {
|
|
pn = expr;
|
|
do {
|
|
n--;
|
|
if (pn->type & 1) {
|
|
end_pushes();
|
|
ins_byte(F_EXPAND_VARARGS);
|
|
ins_byte(n);
|
|
}
|
|
} while ((pn = pn->r.expr));
|
|
}
|
|
}
|
|
|
|
static void
|
|
generate_lvalue_list (parse_node_t * expr) {
|
|
while ((expr = expr->r.expr)) {
|
|
i_generate_node(expr->l.expr);
|
|
end_pushes();
|
|
ins_byte(F_VOID_ASSIGN);
|
|
}
|
|
}
|
|
|
|
INLINE_STATIC void
|
|
switch_to_line (unsigned int line) {
|
|
unsigned int sz = CURRENT_PROGRAM_SIZE - last_size_generated;
|
|
ADDRESS_TYPE s;
|
|
unsigned char *p;
|
|
|
|
if (sz) {
|
|
s = line_being_generated;
|
|
|
|
last_size_generated += sz;
|
|
while (sz > 255) {
|
|
p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, sizeof(ADDRESS_TYPE) + 1);
|
|
*p++ = 255;
|
|
#if !defined(USE_32BIT_ADDRESSES)
|
|
STORE_SHORT(p, s);
|
|
#else
|
|
STORE4(p, s);
|
|
#endif
|
|
sz -= 255;
|
|
}
|
|
p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, sizeof(ADDRESS_TYPE) + 1);
|
|
*p++ = sz;
|
|
#if !defined(USE_32BIT_ADDRESSES)
|
|
STORE_SHORT(p, s);
|
|
#else
|
|
STORE4(p, s);
|
|
#endif
|
|
}
|
|
line_being_generated = line;
|
|
}
|
|
|
|
static int
|
|
try_to_push (int kind, int value) {
|
|
if (push_state) {
|
|
if (value <= PUSH_MASK) {
|
|
if (push_state == 1)
|
|
initialize_push();
|
|
push_state++;
|
|
ins_byte(kind | value);
|
|
if (push_state == 255)
|
|
end_pushes();
|
|
return 1;
|
|
} else end_pushes();
|
|
} else if (value <= PUSH_MASK) {
|
|
push_start = CURRENT_PROGRAM_SIZE;
|
|
push_state = 1;
|
|
switch (kind) {
|
|
case PUSH_STRING: ins_byte(F_SHORT_STRING); break;
|
|
case PUSH_LOCAL: ins_byte(F_LOCAL); break;
|
|
case PUSH_GLOBAL: ins_byte(F_GLOBAL); break;
|
|
case PUSH_NUMBER:
|
|
if (value == 0) {
|
|
ins_byte(F_CONST0);
|
|
return 1;
|
|
} else if (value == 1) {
|
|
ins_byte(F_CONST1);
|
|
return 1;
|
|
}
|
|
ins_byte(F_BYTE);
|
|
}
|
|
if (kind == PUSH_GLOBAL) {
|
|
INS_GLOBAL_INDEX(value);
|
|
} else {
|
|
ins_byte(value);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
i_generate_node (parse_node_t * expr) {
|
|
if (!expr) return;
|
|
|
|
if (expr->line && expr->line != line_being_generated)
|
|
switch_to_line(expr->line);
|
|
switch (expr->kind) {
|
|
case NODE_FUNCTION:
|
|
{
|
|
unsigned short num;
|
|
|
|
/* In the following we know that this function wins all the */
|
|
/* rest - Sym */
|
|
|
|
num = FUNCTION_TEMP(expr->v.number)->u.index;
|
|
FUNC(num)->address = generate_function(FUNC(num),
|
|
expr->r.expr, expr->l.number);
|
|
break;
|
|
}
|
|
case NODE_TERNARY_OP:
|
|
i_generate_node(expr->l.expr);
|
|
expr = expr->r.expr;
|
|
case NODE_BINARY_OP:
|
|
i_generate_node(expr->l.expr);
|
|
/* fall through */
|
|
case NODE_UNARY_OP:
|
|
i_generate_node(expr->r.expr);
|
|
/* fall through */
|
|
case NODE_OPCODE:
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
break;
|
|
case NODE_TERNARY_OP_1:
|
|
i_generate_node(expr->l.expr);
|
|
expr = expr->r.expr;
|
|
/* fall through */
|
|
case NODE_BINARY_OP_1:
|
|
i_generate_node(expr->l.expr);
|
|
i_generate_node(expr->r.expr);
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
ins_byte(expr->type);
|
|
break;
|
|
case NODE_UNARY_OP_1:
|
|
i_generate_node(expr->r.expr);
|
|
/* fall through */
|
|
case NODE_OPCODE_1:
|
|
if (expr->v.number == F_LOCAL) {
|
|
if (try_to_push(PUSH_LOCAL, expr->l.number)) break;
|
|
} else if (expr->v.number == F_GLOBAL) {
|
|
if (try_to_push(PUSH_GLOBAL, expr->l.number)) break;
|
|
}
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
|
|
if ((expr->v.number == F_GLOBAL) ||
|
|
(expr->v.number == F_GLOBAL_LVALUE)) {
|
|
INS_GLOBAL_INDEX(expr->l.number);
|
|
} else {
|
|
ins_byte(expr->l.number);
|
|
}
|
|
|
|
break;
|
|
case NODE_OPCODE_2:
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
ins_byte(expr->l.number);
|
|
if (expr->v.number == F_LOOP_COND_NUMBER)
|
|
#if SIZEOF_LONG == 8
|
|
ins_long(expr->r.number);
|
|
#else
|
|
ins_int(expr->r.number);
|
|
#endif
|
|
else
|
|
ins_byte(expr->r.number);
|
|
break;
|
|
case NODE_RETURN:
|
|
{
|
|
int n;
|
|
n = foreach_depth;
|
|
end_pushes();
|
|
while (n--)
|
|
ins_byte(F_EXIT_FOREACH);
|
|
|
|
if (expr->r.expr) {
|
|
i_generate_node(expr->r.expr);
|
|
end_pushes();
|
|
ins_byte(F_RETURN);
|
|
} else ins_byte(F_RETURN_ZERO);
|
|
break;
|
|
}
|
|
case NODE_STRING:
|
|
if (try_to_push(PUSH_STRING, expr->v.number)) break;
|
|
if (expr->v.number <= 0xff) {
|
|
ins_byte(F_SHORT_STRING);
|
|
ins_byte(expr->v.number);
|
|
} else {
|
|
ins_byte(F_STRING);
|
|
ins_short(expr->v.number);
|
|
}
|
|
break;
|
|
case NODE_REAL:
|
|
end_pushes();
|
|
ins_byte(F_REAL);
|
|
ins_real(expr->v.real);
|
|
break;
|
|
case NODE_NUMBER:
|
|
write_number(expr->v.number);
|
|
break;
|
|
case NODE_LAND_LOR:
|
|
i_generate_node(expr->l.expr);
|
|
i_generate_forward_branch(expr->v.number);
|
|
i_generate_node(expr->r.expr);
|
|
if (expr->l.expr->kind == NODE_BRANCH_LINK)
|
|
i_update_forward_branch_links(expr->v.number,expr->l.expr);
|
|
else
|
|
i_update_forward_branch("&& or ||");
|
|
break;
|
|
case NODE_BRANCH_LINK:
|
|
i_generate_node(expr->l.expr);
|
|
end_pushes();
|
|
ins_byte(0);
|
|
expr->v.number = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
i_generate_node(expr->r.expr);
|
|
break;
|
|
case NODE_CALL_2:
|
|
generate_expr_list(expr->r.expr);
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
ins_byte(expr->l.number >> 16);
|
|
ins_short(expr->l.number & 0xffff);
|
|
ins_byte((expr->r.expr ? expr->r.expr->kind : 0));
|
|
break;
|
|
case NODE_CALL_1:
|
|
generate_expr_list(expr->r.expr);
|
|
end_pushes();
|
|
if (expr->v.number == F_CALL_FUNCTION_BY_ADDRESS) {
|
|
expr->l.number = comp_def_index_map[expr->l.number];
|
|
}
|
|
ins_byte(expr->v.number);
|
|
ins_short(expr->l.number);
|
|
ins_byte((expr->r.expr ? expr->r.expr->kind : 0));
|
|
break;
|
|
case NODE_CALL:
|
|
generate_expr_list(expr->r.expr);
|
|
end_pushes();
|
|
ins_byte(expr->v.number);
|
|
ins_short(expr->l.number);
|
|
break;
|
|
case NODE_TWO_VALUES:
|
|
i_generate_node(expr->l.expr);
|
|
i_generate_node(expr->r.expr);
|
|
break;
|
|
case NODE_CONTROL_JUMP:
|
|
{
|
|
int kind = expr->v.number;
|
|
end_pushes();
|
|
ins_byte(F_BRANCH);
|
|
expr->v.expr = branch_list[kind];
|
|
expr->l.number = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
branch_list[kind] = expr;
|
|
break;
|
|
}
|
|
case NODE_PARAMETER:
|
|
{
|
|
int which = expr->v.number + current_num_values;
|
|
if (try_to_push(PUSH_LOCAL, which)) break;
|
|
ins_byte(F_LOCAL);
|
|
ins_byte(which);
|
|
break;
|
|
}
|
|
case NODE_PARAMETER_LVALUE:
|
|
end_pushes();
|
|
ins_byte(F_LOCAL_LVALUE);
|
|
ins_byte(expr->v.number + current_num_values);
|
|
break;
|
|
case NODE_IF:
|
|
i_generate_if_branch(expr->v.expr, 0);
|
|
i_generate_node(expr->l.expr);
|
|
if (expr->r.expr) {
|
|
i_generate_else();
|
|
i_generate_node(expr->r.expr);
|
|
}
|
|
i_update_forward_branch("if");
|
|
break;
|
|
case NODE_LOOP:
|
|
i_generate_loop(expr->type, expr->v.expr, expr->l.expr, expr->r.expr);
|
|
break;
|
|
case NODE_FOREACH:
|
|
{
|
|
int tmp = 0;
|
|
int left_is_global = 0;
|
|
|
|
i_generate_node(expr->v.expr);
|
|
end_pushes();
|
|
ins_byte(F_FOREACH);
|
|
if (expr->l.expr->v.number == F_GLOBAL_LVALUE)
|
|
tmp |= FOREACH_RIGHT_GLOBAL;
|
|
else if (expr->l.expr->v.number == F_REF_LVALUE)
|
|
tmp |= FOREACH_REF;
|
|
if (expr->r.expr) {
|
|
if (tmp & FOREACH_RIGHT_GLOBAL)
|
|
tmp = (tmp & ~FOREACH_RIGHT_GLOBAL) | FOREACH_LEFT_GLOBAL;
|
|
|
|
tmp |= FOREACH_MAPPING;
|
|
if (expr->r.expr->v.number == F_GLOBAL_LVALUE)
|
|
tmp |= FOREACH_RIGHT_GLOBAL;
|
|
else if (expr->r.expr->v.number == F_REF_LVALUE)
|
|
tmp |= FOREACH_REF;
|
|
}
|
|
ins_byte(tmp);
|
|
|
|
if (tmp & FOREACH_MAPPING) {
|
|
if (tmp & FOREACH_LEFT_GLOBAL) {
|
|
left_is_global = 1;
|
|
}
|
|
} else {
|
|
if (tmp & FOREACH_RIGHT_GLOBAL) {
|
|
left_is_global = 1;
|
|
}
|
|
}
|
|
|
|
if (left_is_global == 1) {
|
|
INS_GLOBAL_INDEX(expr->l.expr->l.number);
|
|
} else {
|
|
ins_byte(expr->l.expr->l.number);
|
|
}
|
|
|
|
if (expr->r.expr) {
|
|
|
|
if (tmp & FOREACH_RIGHT_GLOBAL) {
|
|
INS_GLOBAL_INDEX(expr->r.expr->l.number);
|
|
} else {
|
|
ins_byte(expr->r.expr->l.number);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NODE_CASE_NUMBER:
|
|
case NODE_CASE_STRING:
|
|
if (expr->v.expr) {
|
|
parse_node_t *other = expr->v.expr;
|
|
expr->v.number = 1;
|
|
other->l.expr = expr->l.expr;
|
|
other->v.number = CURRENT_PROGRAM_SIZE;
|
|
expr->l.expr = other;
|
|
} else {
|
|
expr->v.number = CURRENT_PROGRAM_SIZE;
|
|
}
|
|
end_pushes();
|
|
break;
|
|
case NODE_DEFAULT:
|
|
expr->v.number = CURRENT_PROGRAM_SIZE;
|
|
end_pushes();
|
|
break;
|
|
case NODE_SWITCH_STRINGS:
|
|
case NODE_SWITCH_NUMBERS:
|
|
case NODE_SWITCH_DIRECT:
|
|
case NODE_SWITCH_RANGES:
|
|
{
|
|
long addr, last_break;
|
|
parse_node_t *sub = expr->l.expr;
|
|
parse_node_t *save_switch_breaks = branch_list[CJ_BREAK_SWITCH];
|
|
|
|
i_generate_node(sub);
|
|
branch_list[CJ_BREAK_SWITCH] = 0;
|
|
end_pushes();
|
|
ins_byte(F_SWITCH);
|
|
addr = CURRENT_PROGRAM_SIZE;
|
|
/* all these are addresses in here are relative to addr now,
|
|
* which is also the value of pc in f_switch(). Note that
|
|
* addr is one less than it was in older drivers.
|
|
*/
|
|
ins_byte(0xff); /* kind of table */
|
|
ins_short(0); /* address of table */
|
|
ins_short(0); /* end of table */
|
|
ins_short(0); /* default address */
|
|
i_generate_node(expr->r.expr);
|
|
if (expr->v.expr && expr->v.expr->kind == NODE_DEFAULT) {
|
|
upd_short(addr + 5, expr->v.expr->v.number - addr,
|
|
"switch");
|
|
expr->v.expr = expr->v.expr->l.expr;
|
|
} else {
|
|
upd_short(addr + 5, CURRENT_PROGRAM_SIZE - addr, "switch");
|
|
}
|
|
/* just in case the last case doesn't have a break */
|
|
end_pushes();
|
|
ins_byte(F_BRANCH);
|
|
last_break = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
/* build table */
|
|
upd_short(addr + 1, CURRENT_PROGRAM_SIZE - addr, "switch");
|
|
|
|
if (expr->kind == NODE_SWITCH_DIRECT) {
|
|
parse_node_t *pn = expr->v.expr;
|
|
while (pn) {
|
|
ins_rel_short(pn->v.number - addr);
|
|
pn = pn->l.expr;
|
|
}
|
|
ins_int(expr->v.expr->r.number);
|
|
mem_block[A_PROGRAM].block[addr] = (char)0xfe;
|
|
} else {
|
|
int table_size = 0;
|
|
int power_of_two = 1;
|
|
int i = 0;
|
|
parse_node_t *pn = expr->v.expr;
|
|
|
|
while (pn) {
|
|
if (expr->kind == NODE_SWITCH_STRINGS) {
|
|
if (pn->r.number) {
|
|
INS_POINTER((POINTER_INT)
|
|
PROG_STRING(pn->r.number));
|
|
} else
|
|
INS_POINTER((POINTER_INT)0);
|
|
} else
|
|
INS_POINTER((POINTER_INT)pn->r.expr);
|
|
if (pn->v.number == 1)
|
|
ins_short(1);
|
|
else
|
|
ins_rel_short(pn->v.number - addr);
|
|
pn = pn->l.expr;
|
|
table_size += 1;
|
|
}
|
|
while ((power_of_two<<1) <= table_size) {
|
|
power_of_two <<= 1;
|
|
i++;
|
|
}
|
|
if (expr->kind != NODE_SWITCH_STRINGS)
|
|
mem_block[A_PROGRAM].block[addr] = (char)(0xf0+i);
|
|
else
|
|
mem_block[A_PROGRAM].block[addr] = (char)(i*0x10+0x0f);
|
|
}
|
|
i_update_branch_list(branch_list[CJ_BREAK_SWITCH], "switch break");
|
|
branch_list[CJ_BREAK_SWITCH] = save_switch_breaks;
|
|
upd_short(last_break, CURRENT_PROGRAM_SIZE - last_break, "switch break");
|
|
upd_short(addr+3, CURRENT_PROGRAM_SIZE - addr, "switch");
|
|
break;
|
|
}
|
|
case NODE_CATCH:
|
|
{
|
|
int addr;
|
|
|
|
end_pushes();
|
|
ins_byte(F_CATCH);
|
|
addr = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
i_generate_node(expr->r.expr);
|
|
ins_byte(F_END_CATCH);
|
|
upd_short(addr, CURRENT_PROGRAM_SIZE - addr, "catch");
|
|
break;
|
|
}
|
|
case NODE_TIME_EXPRESSION:
|
|
{
|
|
end_pushes();
|
|
ins_byte(F_TIME_EXPRESSION);
|
|
i_generate_node(expr->r.expr);
|
|
ins_byte(F_END_TIME_EXPRESSION);
|
|
break;
|
|
}
|
|
case NODE_LVALUE_EFUN:
|
|
i_generate_node(expr->l.expr);
|
|
generate_lvalue_list(expr->r.expr);
|
|
break;
|
|
case NODE_FUNCTION_CONSTRUCTOR:
|
|
if (expr->r.expr) {
|
|
generate_expr_list(expr->r.expr);
|
|
end_pushes();
|
|
ins_byte(F_AGGREGATE);
|
|
ins_short(expr->r.expr->kind);
|
|
} else {
|
|
end_pushes();
|
|
ins_byte(F_CONST0);
|
|
}
|
|
end_pushes();
|
|
|
|
ins_byte(F_FUNCTION_CONSTRUCTOR);
|
|
ins_byte(expr->v.number & 0xff);
|
|
|
|
switch (expr->v.number & 0xff) {
|
|
case FP_SIMUL:
|
|
ins_short(expr->v.number >> 8);
|
|
break;
|
|
case FP_LOCAL:
|
|
ins_short(comp_def_index_map[expr->v.number >> 8]);
|
|
break;
|
|
case FP_EFUN:
|
|
ins_short(predefs[expr->v.number >> 8].token);
|
|
break;
|
|
case FP_FUNCTIONAL:
|
|
case FP_FUNCTIONAL | FP_NOT_BINDABLE:
|
|
{
|
|
int addr, save_current_num_values = current_num_values;
|
|
ins_byte(expr->v.number >> 8);
|
|
addr = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
current_num_values = expr->r.expr ? expr->r.expr->kind : 0;
|
|
i_generate_node(expr->l.expr);
|
|
current_num_values = save_current_num_values;
|
|
end_pushes();
|
|
ins_byte(F_RETURN);
|
|
upd_short(addr, CURRENT_PROGRAM_SIZE - addr - 2,
|
|
"function pointer");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case NODE_ANON_FUNC:
|
|
{
|
|
int addr;
|
|
int save_fd = foreach_depth;
|
|
|
|
foreach_depth = 0;
|
|
end_pushes();
|
|
ins_byte(F_FUNCTION_CONSTRUCTOR);
|
|
if (expr->v.number & 0x10000)
|
|
ins_byte(FP_ANONYMOUS | FP_NOT_BINDABLE);
|
|
else
|
|
ins_byte(FP_ANONYMOUS);
|
|
ins_byte(expr->v.number & 0xff);
|
|
ins_byte(expr->l.number);
|
|
addr = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
i_generate_node(expr->r.expr);
|
|
upd_short(addr, CURRENT_PROGRAM_SIZE - addr - 2,
|
|
"function pointer");
|
|
foreach_depth = save_fd;
|
|
break;
|
|
}
|
|
case NODE_EFUN:
|
|
{
|
|
int novalue_used = expr->v.number & NOVALUE_USED_FLAG;
|
|
int f = expr->v.number & ~NOVALUE_USED_FLAG;
|
|
|
|
generate_expr_list(expr->r.expr);
|
|
end_pushes();
|
|
if (f < ONEARG_MAX) {
|
|
ins_byte(f);
|
|
} else {
|
|
/* max_arg == -1 must use F_EFUNV so that varargs expansion works*/
|
|
if (expr->l.number < 4 && instrs[f].max_arg != -1)
|
|
ins_byte(F_EFUN0 + expr->l.number);
|
|
else {
|
|
ins_byte(F_EFUNV);
|
|
ins_byte(expr->l.number);
|
|
}
|
|
ins_byte(f - ONEARG_MAX);
|
|
}
|
|
if (novalue_used) {
|
|
/* the value of a void efun was used. Put in a zero. */
|
|
ins_byte(F_CONST0);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
fatal("Unknown node %i in i_generate_node.\n", expr->kind);
|
|
}
|
|
}
|
|
|
|
static void i_generate_loop (int test_first, parse_node_t * block,
|
|
parse_node_t * inc, parse_node_t * test) {
|
|
parse_node_t *save_breaks = branch_list[CJ_BREAK];
|
|
parse_node_t *save_continues = branch_list[CJ_CONTINUE];
|
|
int forever = node_always_true(test);
|
|
int pos;
|
|
|
|
if (test_first == 2) foreach_depth++;
|
|
branch_list[CJ_BREAK] = branch_list[CJ_CONTINUE] = 0;
|
|
end_pushes();
|
|
if (!forever && test_first)
|
|
i_generate_forward_branch(F_BRANCH);
|
|
pos = CURRENT_PROGRAM_SIZE;
|
|
i_generate_node(block);
|
|
i_update_branch_list(branch_list[CJ_CONTINUE], "continue");
|
|
if (inc) i_generate_node(inc);
|
|
if (!forever && test_first) i_update_forward_branch("loop");
|
|
if (test && (test->v.number == F_LOOP_COND_LOCAL ||
|
|
test->v.number == F_LOOP_COND_NUMBER ||
|
|
test->v.number == F_NEXT_FOREACH)) {
|
|
i_generate_node(test);
|
|
ins_short(CURRENT_PROGRAM_SIZE - pos);
|
|
} else i_branch_backwards(generate_conditional_branch(test), pos);
|
|
i_update_branch_list(branch_list[CJ_BREAK], "break");
|
|
branch_list[CJ_BREAK] = save_breaks;
|
|
branch_list[CJ_CONTINUE] = save_continues;
|
|
if (test_first == 2) foreach_depth--;
|
|
}
|
|
|
|
static void
|
|
i_generate_if_branch (parse_node_t * node, int invert) {
|
|
int generate_both = 0;
|
|
int branch = (invert ? F_BRANCH_WHEN_NON_ZERO : F_BRANCH_WHEN_ZERO);
|
|
|
|
switch (node->kind) {
|
|
case NODE_UNARY_OP:
|
|
if (node->v.number == F_NOT) {
|
|
i_generate_if_branch(node->r.expr, !invert);
|
|
return;
|
|
}
|
|
break;
|
|
case NODE_BINARY_OP:
|
|
switch (node->v.number) {
|
|
case F_EQ:
|
|
generate_both = 1;
|
|
branch = (invert ? F_BRANCH_EQ : F_BRANCH_NE);
|
|
break;
|
|
case F_GE:
|
|
if (invert) {
|
|
generate_both = 1;
|
|
branch = F_BRANCH_GE;
|
|
}
|
|
break;
|
|
case F_LE:
|
|
if (invert) {
|
|
generate_both = 1;
|
|
branch = F_BRANCH_LE;
|
|
}
|
|
break;
|
|
case F_LT:
|
|
if (!invert) {
|
|
generate_both = 1;
|
|
branch = F_BRANCH_GE;
|
|
}
|
|
break;
|
|
case F_GT:
|
|
if (!invert) {
|
|
generate_both = 1;
|
|
branch = F_BRANCH_LE;
|
|
}
|
|
break;
|
|
case F_NE:
|
|
generate_both = 1;
|
|
branch = (invert ? F_BRANCH_NE : F_BRANCH_EQ);
|
|
break;
|
|
}
|
|
}
|
|
if (generate_both) {
|
|
i_generate_node(node->l.expr);
|
|
i_generate_node(node->r.expr);
|
|
} else {
|
|
i_generate_node(node);
|
|
}
|
|
i_generate_forward_branch(branch);
|
|
}
|
|
|
|
void
|
|
i_generate_inherited_init_call (int index, short f) {
|
|
end_pushes();
|
|
ins_byte(F_CALL_INHERITED);
|
|
ins_byte(index);
|
|
ins_short(f);
|
|
ins_byte(0);
|
|
ins_byte(F_POP_VALUE);
|
|
}
|
|
|
|
void i_generate_forward_branch (char b) {
|
|
end_pushes();
|
|
ins_byte(b);
|
|
if (nforward_branches == nforward_branches_max) {
|
|
nforward_branches_max += 10;
|
|
forward_branches = RESIZE(forward_branches, nforward_branches_max, int,
|
|
TAG_COMPILER, "forward_branches");
|
|
}
|
|
|
|
forward_branches[nforward_branches++] = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
}
|
|
|
|
void
|
|
i_update_forward_branch (const char * what) {
|
|
end_pushes();
|
|
nforward_branches--;
|
|
upd_short(forward_branches[nforward_branches],
|
|
CURRENT_PROGRAM_SIZE - forward_branches[nforward_branches],
|
|
what);
|
|
}
|
|
|
|
void i_update_forward_branch_links (char kind, parse_node_t * link_start) {
|
|
int i;
|
|
|
|
end_pushes();
|
|
nforward_branches--;
|
|
upd_short(forward_branches[nforward_branches],
|
|
CURRENT_PROGRAM_SIZE - forward_branches[nforward_branches],
|
|
"&& or ||");
|
|
do {
|
|
i = link_start->v.number;
|
|
upd_byte(i-1, kind);
|
|
upd_short(i, CURRENT_PROGRAM_SIZE - i, "&& or ||");
|
|
link_start = link_start->l.expr;
|
|
} while (link_start->kind == NODE_BRANCH_LINK);
|
|
}
|
|
|
|
void
|
|
i_branch_backwards (char b, int addr) {
|
|
end_pushes();
|
|
if (b) {
|
|
if (b != F_WHILE_DEC)
|
|
ins_byte(b);
|
|
ins_short(CURRENT_PROGRAM_SIZE - addr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
i_update_branch_list (parse_node_t * bl, const char * what) {
|
|
int current_size;
|
|
|
|
end_pushes();
|
|
current_size = CURRENT_PROGRAM_SIZE;
|
|
|
|
while (bl) {
|
|
upd_short(bl->l.number, current_size - bl->l.number, what);
|
|
bl = bl->v.expr;
|
|
}
|
|
}
|
|
|
|
void
|
|
i_generate_else() {
|
|
int old;
|
|
|
|
old = forward_branches[nforward_branches - 1];
|
|
|
|
/* set up a new branch to after the end of the if */
|
|
end_pushes();
|
|
ins_byte(F_BRANCH);
|
|
forward_branches[nforward_branches-1] = CURRENT_PROGRAM_SIZE;
|
|
ins_short(0);
|
|
|
|
/* update the old branch to point to this point */
|
|
upd_short(old, CURRENT_PROGRAM_SIZE - old, "if");
|
|
}
|
|
|
|
void
|
|
i_initialize_parser() {
|
|
foreach_depth = 0;
|
|
|
|
if (!forward_branches) {
|
|
forward_branches = CALLOCATE(10, int, TAG_COMPILER, "forward_branches");
|
|
nforward_branches_max = 10;
|
|
}
|
|
nforward_branches = 0;
|
|
|
|
branch_list[CJ_BREAK] = 0;
|
|
branch_list[CJ_BREAK_SWITCH] = 0;
|
|
branch_list[CJ_CONTINUE] = 0;
|
|
|
|
prog_code = mem_block[A_PROGRAM].block;
|
|
prog_code_max = mem_block[A_PROGRAM].block + mem_block[A_PROGRAM].max_size;
|
|
|
|
line_being_generated = 0;
|
|
last_size_generated = 0;
|
|
}
|
|
|
|
void
|
|
i_uninitialize_parser() {
|
|
#ifdef DEBUGMALLOC_EXTENSIONS
|
|
/*
|
|
* The intention is to keep the allocated space for forward branches
|
|
* hanging around between compiles so we don't have to keep allocating it.
|
|
* The problem is that with DEBUGMALLOC_EXENTIONS compiled in, it'll be
|
|
* reported as a memory leak, so clean it up here. Yes, it's a performance
|
|
* hit, but we're trying to debug not run a race, so deal with it.
|
|
* -- Marius, 11-Aug-2000
|
|
*/
|
|
if (forward_branches) {
|
|
FREE(forward_branches);
|
|
forward_branches = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
i_generate_final_program (int x) {
|
|
if (!x) {
|
|
UPDATE_PROGRAM_SIZE;
|
|
/* This needs more work */
|
|
// if (pragmas & PRAGMA_OPTIMIZE)
|
|
// optimize_icode(0, 0, 0);
|
|
|
|
save_file_info(current_file_id, current_line - current_line_saved);
|
|
switch_to_line(-1); /* generate line numbers for the end */
|
|
}
|
|
}
|
|
|
|
/* Currently, this procedure handles:
|
|
* - jump threading
|
|
*/
|
|
void
|
|
optimize_icode (char * start, char * pc, char * end) {
|
|
int instr = 0, prev;
|
|
if (start == 0) {
|
|
/* we don't optimize the initializer block right now b/c all the
|
|
* stuff we do (jump threading, etc) can't occur there.
|
|
*/
|
|
start = mem_block[A_PROGRAM].block;
|
|
pc = start;
|
|
end = pc + mem_block[A_PROGRAM].current_size;
|
|
if (*pc == 0) {
|
|
/* no initializer jump */
|
|
pc += 3;
|
|
}
|
|
}
|
|
while (pc < end) {
|
|
prev = instr;
|
|
switch (instr = EXTRACT_UCHAR(pc++)) {
|
|
#if SIZEOF_LONG == 4
|
|
case F_NUMBER:
|
|
#endif
|
|
case F_REAL:
|
|
case F_CALL_INHERITED:
|
|
pc += 4;
|
|
break;
|
|
#if SIZEOF_LONG == 8
|
|
case F_NUMBER:
|
|
pc += 8;
|
|
break;
|
|
#endif
|
|
case F_SIMUL_EFUN:
|
|
case F_CALL_FUNCTION_BY_ADDRESS:
|
|
pc += 3;
|
|
break;
|
|
case F_BRANCH_NE:
|
|
case F_BRANCH_GE:
|
|
case F_BRANCH_LE:
|
|
case F_BRANCH_EQ:
|
|
case F_BRANCH:
|
|
case F_BRANCH_WHEN_ZERO:
|
|
case F_BRANCH_WHEN_NON_ZERO:
|
|
case F_BBRANCH:
|
|
case F_BBRANCH_WHEN_ZERO:
|
|
case F_BBRANCH_WHEN_NON_ZERO:
|
|
case F_BBRANCH_LT:
|
|
{
|
|
char *tmp;
|
|
short sarg;
|
|
/* thread jumps */
|
|
COPY_SHORT(&sarg, pc);
|
|
if (instr > F_BRANCH)
|
|
tmp = pc - sarg;
|
|
else
|
|
tmp = pc + sarg;
|
|
sarg = 0;
|
|
while (1) {
|
|
if (EXTRACT_UCHAR(tmp) == F_BRANCH) {
|
|
COPY_SHORT(&sarg, tmp + 1);
|
|
tmp += sarg + 1;
|
|
} else if (EXTRACT_UCHAR(tmp) == F_BBRANCH) {
|
|
COPY_SHORT(&sarg, tmp + 1);
|
|
tmp -= sarg - 1;
|
|
} else break;
|
|
}
|
|
if (!sarg) {
|
|
pc += 2;
|
|
break;
|
|
}
|
|
/* be careful; in the process of threading a forward jump
|
|
* may have changed to a reverse one or vice versa
|
|
*/
|
|
if (tmp > pc) {
|
|
if (instr > F_BRANCH) {
|
|
pc[-1] -= 3; /* change to forward branch */
|
|
}
|
|
sarg = tmp - pc;
|
|
} else {
|
|
if (instr <= F_BRANCH) {
|
|
pc[-1] += 3; /* change to backwards branch */
|
|
}
|
|
sarg = pc - tmp;
|
|
}
|
|
STORE_SHORT(pc, sarg);
|
|
break;
|
|
}
|
|
#ifdef F_LOR
|
|
case F_LOR:
|
|
case F_LAND:
|
|
{
|
|
char *tmp;
|
|
short sarg;
|
|
/* thread jumps */
|
|
COPY_SHORT(&sarg, pc);
|
|
tmp = pc + sarg;
|
|
sarg = 0;
|
|
while (1) {
|
|
if (EXTRACT_UCHAR(tmp) == F_BRANCH) {
|
|
COPY_SHORT(&sarg, tmp + 1);
|
|
tmp += sarg + 1;
|
|
} else if (EXTRACT_UCHAR(tmp) == F_BBRANCH) {
|
|
COPY_SHORT(&sarg, tmp + 1);
|
|
tmp -= sarg - 1;
|
|
} else break;
|
|
}
|
|
if (!sarg) {
|
|
pc += 2;
|
|
break;
|
|
}
|
|
/* be careful; in the process of threading a forward jump
|
|
* may have changed to a reverse one or vice versa
|
|
*/
|
|
if (tmp > pc) {
|
|
sarg = tmp - pc;
|
|
} else {
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"Optimization failed; can't || or && backwards.\n");
|
|
#endif
|
|
pc += 2;
|
|
break;
|
|
}
|
|
STORE_SHORT(pc, sarg);
|
|
break;
|
|
}
|
|
#endif
|
|
case F_CATCH:
|
|
case F_AGGREGATE:
|
|
case F_AGGREGATE_ASSOC:
|
|
case F_NEXT_FOREACH:
|
|
case F_GLOBAL:
|
|
case F_GLOBAL_LVALUE:
|
|
case F_STRING:
|
|
#ifdef F_JUMP_WHEN_ZERO
|
|
case F_JUMP_WHEN_ZERO:
|
|
case F_JUMP_WHEN_NON_ZERO:
|
|
#endif
|
|
#ifdef F_JUMP
|
|
case F_JUMP:
|
|
#endif
|
|
pc += 2;
|
|
break;
|
|
case F_SHORT_STRING:
|
|
case F_LOOP_INCR:
|
|
case F_WHILE_DEC:
|
|
case F_LOCAL:
|
|
case F_LOCAL_LVALUE:
|
|
case F_REF:
|
|
case F_REF_LVALUE:
|
|
case F_SSCANF:
|
|
case F_PARSE_COMMAND:
|
|
case F_BYTE:
|
|
case F_NBYTE:
|
|
case F_TRANSFER_LOCAL:
|
|
pc++;
|
|
break;
|
|
case F_FUNCTION_CONSTRUCTOR:
|
|
switch (EXTRACT_UCHAR(pc++)) {
|
|
case FP_SIMUL:
|
|
case FP_LOCAL:
|
|
pc += 2;
|
|
break;
|
|
case FP_FUNCTIONAL:
|
|
case FP_FUNCTIONAL | FP_NOT_BINDABLE:
|
|
pc += 3;
|
|
break;
|
|
case FP_ANONYMOUS:
|
|
case FP_ANONYMOUS | FP_NOT_BINDABLE:
|
|
pc += 4;
|
|
break;
|
|
case FP_EFUN:
|
|
pc += 2;
|
|
break;
|
|
}
|
|
break;
|
|
case F_SWITCH:
|
|
{
|
|
unsigned short stable, etable;
|
|
char *swstart = pc;
|
|
pc++; /* table type */
|
|
LOAD_SHORT(stable, pc);
|
|
LOAD_SHORT(etable, pc);
|
|
pc += 2; /* def */
|
|
//break; //doesn't seem to work!
|
|
printf("stable: %x pc %x swstart %x etable %x\n", stable, pc, swstart, etable);
|
|
DEBUG_CHECK(stable < pc - swstart || etable < pc - swstart
|
|
|| etable < stable,
|
|
"Error in switch table found while optimizing\n");
|
|
/* recursively optimize the inside of the switch */
|
|
optimize_icode(start, pc, start + stable);
|
|
pc = swstart + etable;
|
|
break;
|
|
}
|
|
case F_PUSH:
|
|
{
|
|
pc+=EXTRACT_UCHAR(pc);
|
|
pc++;
|
|
break;
|
|
}
|
|
case F_EFUNV:
|
|
pc++;
|
|
case F_EFUN0:
|
|
case F_EFUN1:
|
|
case F_EFUN2:
|
|
case F_EFUN3:
|
|
instr = EXTRACT_UCHAR(pc++) + ONEARG_MAX;
|
|
default:
|
|
if(instr < BASE || instr > NUM_OPCODES)
|
|
printf("instr %d prev %d\n", instr, prev);
|
|
if(!instr || instr > NUM_OPCODES)
|
|
fatal("illegal opcode");
|
|
prev = instr;
|
|
// if ((instr >= BASE) &&
|
|
// (instrs[instr].min_arg != instrs[instr].max_arg))
|
|
// pc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|