home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_300
/
357_01
/
cstar1.exe
/
G1.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-18
|
17KB
|
830 lines
/*
C* -- Code generation -- machine independent part.
source: g1.c
started: November 9, 1985
version:
March 6, 1987
February 22, 1989:
all out_* routines moved to out.c
out_nl, out_lit replaced by sysnlput, syssput
PUBLIC DOMAIN SOFTWARE
The CSTAR program was placed in the public domain on June 15, 1991,
by its author and sole owner,
Edward K. Ream
1617 Monroe Street
Madison, WI 53711
(608) 257-0802
CSTAR may be used for any commercial or non-commercial purpose.
See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
*/
#include "cstar.h"
/*
Externally visible functions
*/
void gen_init(void);
void gen_function(struct fbody *p);
/*
Internal functions
*/
static void gen_do (struct node *p);
static void gen_if (struct node *p);
static void gen_for (struct node *p);
static void gen_switch (struct node *p);
static void gen_while (struct node *p);
static void gen_break (struct node *p);
static void gen_case (struct node *p);
static void gen_continue (struct node *p);
static void gen_default (struct node *p);
static void gen_return (struct node *p);
static void gen_goto (struct node *p);
static void gen_label (struct node *p);
static void gen_ldef (struct node *p);
static void gen_x (struct node *p);
static void gen_z (struct node *p);
static bool is_vbool (struct node *p);
/*
Shared register data
*/
extern int sa_used [8];
extern int sd_used [8];
extern int sa_push[8];
extern int sd_push[8];
#define g_bra(p) g_1lab(X_BRA, p)
/*
Data structures used only by this module.
*/
static struct node * brk_lab;
static struct node * cont_lab;
static struct node * ret_lab;
static int ret_a, ret_d;
/*
Initialize this module.
*/
void
gen_init(void)
{
TICK("gen_init");
brk_lab = NULL;
cont_lab = NULL;
ret_lab = NULL;
code_head = NULL;
code_tail = NULL;
g_init();
int_type = new_tnode();
byte_type = new_tnode();
byte_type -> t_tsize = 1;
byte_type -> t_mclass |= CHAR_MOD;
long_type = new_tnode();
long_type -> t_tsize = LONG_SIZE;
long_type -> t_mclass |= LONG_MOD;
segment = -1;
}
/*
Generate code for a list of local definitions/declarations.
WARNING: not ready yet.
*/
void
gen_ldef(register struct node *p)
{
TICK("gen_ldef");
}
/*
Append code generated for a list of statements to the global code list.
*/
static void
gen_list(register struct node * p)
{
TRACEPB("gen_list", printf("(%p)\n", p));
for (; p; p = p -> n_next) {
g_line(p -> n_linno);
TRACEP("gen_list",
printf("token %d %s\n", p -> n_type,
ps_tok(p -> n_type)));
switch (p -> n_type) {
case K_IF: gen_if(p); break;
case K_DO: gen_do(p); break;
case K_WHILE: gen_while(p); break;
case K_FOR: gen_for(p); break;
case K_SWITCH: gen_switch(p); break;
case K_CASE: gen_case(p); break;
case K_RETURN: gen_return(p); break;
case K_GOTO: gen_goto(p); break;
case K_BREAK: gen_break(p); break;
case K_CONTINUE: gen_continue(p); break;
case K_DEFAULT: gen_default(p); break;
case Z_TOK: gen_z(p); break;
case X_TOK: gen_x(p); break;
case LABEL_TOK: gen_label(p); break;
default:
/* must be an expr, if not, gen_expr will catch it */
TRACEP("gen_list", printf("expression %p->%p\n",
p, p -> n_cltype));
gen_expr(p);
}
}
TICKX("gen_list");
}
/*
Generate code for a function.
1. Generate the entry code, body, and exit code.
2. Do optimization
3. Output the code
The caller does gen_init.
CAUTION: the long link frame size is of an unsigned type; it is not
signed long. It causes the stack to be offset by an unsigned
negative amount, although it is not itself treated as unsigned
negative. See note in reg.c/decl_alloc().
*/
void
gen_function(register struct fbody * p)
{
register unsigned long link_size;
register int i;
int r_push, do_addq;
static unsigned long t_err = 0;
TRACEPB("gen_function",
printf("%p: %s; call_1arg %d\n", p, p -> fname, call_1arg));
if (tree_flag) {
sysnlput();
syssput(">>>>> Parse Tree for ");
syssput(p -> fname);
syssput("()");
sysnlput();
out_tree(p -> parse);
sysnlput();
}
if (nogen_flag) {
/* Suppress code generation. */
RETURN_VOID("gen_function");
}
if (t_errcount) {
if (t_err == 0) {
/* do something to guarantee an assembler error */
/* this way it shows up on the "comp" printout */
sysnlput();
syssput("&& error");
sysnlput();
}
syssput("* ");
syssput(p -> fname);
sysnlput();
t_err = t_errcount;
RETURN_VOID("gen_function");
}
/* if call_1arg optimization is unwanted, set call_1arg=0 here */
link_size = p -> lcl_size;
/* decide about return register */
TRACEP("gen_function",
printf("function returns type: ");
pr_type(p -> ftype -> t_link);
printf("\n");
);
i = p -> ftype -> t_link -> t_typtok;
if (i == INT_TYPE) {
ret_a = FALSE;
ret_d = TRUE;
}
else if (i != VOID_TYPE) {
#ifdef D0_ONLY
ret_a = FALSE;
ret_d = TRUE;
#else
ret_a = TRUE;
ret_d = FALSE;
#endif /* D0_ONLY */
}
else {
ret_a = ret_d = FALSE;
}
TRACEP("gen_function",printf("ret_d = %d ret_a = %d\n", ret_d,ret_a));
/* Set the global return label for this function. */
ret_lab = new_clabel();
/* Generate the body. */
gen_list(p -> parse);
/* Generate the exit */
g_label(ret_lab);
/* ------------ register allocation ------------ */
/* identify the registers which need to be pushed */
r_push = FALSE;
for (i = 0; i <= 7; i++) {
sa_push[i] = sa_used[i];
sd_push[i] = sd_used[i];
}
TRACEP("gen_function",
printf("used: d:");
for (i = 0; i <= 7; i++) {
printf(" %d", sd_push[i]);
}
printf(" a:");
for (i = 0; i <= 7; i++) {
printf(" %d", sa_push[i]);
}
printf("\n");
);
/* do not push those registers treated as SCRATCH, no matter how
they got used (drawn as temps or named explicitly;)
the caller is responsible for them */
for (i = 0; i <= ASCRATCH; i++) {
sa_push[i] = FALSE;
}
for (i = 0; i <= DSCRATCH; i++) {
sd_push[i] = FALSE;
}
/* do not push the return register, since popping it would
clobber the return */
if (ret_a) {
sa_push[0] = FALSE;
}
else if (ret_d) {
sd_push[0] = FALSE;
}
TRACEP("gen_function",
printf("push: d:");
for (i = 0; i <= 7; i++) {
printf(" %d", sd_push[i]);
}
printf(" a:");
for (i = 0; i <= 7; i++) {
printf(" %d", sa_push[i]);
}
printf("\n");
);
for (i = 0; i <= 7; i++) {
if (sa_push[i] || sd_push[i]) {
r_push = TRUE;
break;
}
}
TRACEP("gen_function", printf("r_push = %d\n", r_push));
/*
insert an extra register to push, or flag an extra addq.
this obviates the need for calls to functions with one
argument to adjust the stack. it saves a significant amount
of code, several percent.
*/
do_addq = FALSE;
/* NOTE: for speed, >= 2 may be better if there are few calls */
if (call_1arg >= 1) {
if (r_push) {
if (ret_d) {
if (sd_push[1]) {
/* no possible extra push */
do_addq = TRUE;
/* WARNING */
t_error("gen_function: case not done yet");
}
else {
sd_push[1] = TRUE;
}
}
else {
/* d0 is the extra push */
sd_push[0] = TRUE;
}
}
else {
link_size += 4;
}
}
out_function(p, link_size, r_push, do_addq);
TICKX("gen_function");
}
/*
Generate code for the do statement.
*/
static void
gen_do(register struct node * p)
{
struct node *loop1_lab, *loop2_lab;
struct node *old_cont, *old_brk;
/* Save old values. */
TRACEPB("gen_do", printf("(%p)\n", p));
old_brk = brk_lab;
old_cont = cont_lab;
/* Get new values. */
loop1_lab = new_clabel();
loop2_lab = new_clabel();
cont_lab = new_clabel();
brk_lab = new_clabel();
gen_pp(p -> n_dbool);
if (is_vbool(p -> n_dbool)) {
g_bra(loop2_lab);
g_label(loop1_lab);
gen_bpost(p -> n_dbool);
g_label(loop2_lab);
gen_list(p -> n_dbdy);
g_label(cont_lab);
gen_bool(p -> n_dbool, loop1_lab, FALL_THROUGH);
gen_bpost(p -> n_dbool);
g_label(brk_lab);
}
else {
g_label(cont_lab);
gen_list(p -> n_dbdy);
g_bra(cont_lab);
g_label(brk_lab);
}
/* Restore old values. */
brk_lab = old_brk;
cont_lab = old_cont;
TICKX("gen_do");
}
/*
Generate code for an "if" statement.
In general, we need to generate an else clause even if none was
given explicitly so that there will be a place to put the sequence
points for each branch of the boolean expression.
*/
static void
gen_if(register struct node * p)
{
struct node *else_lab, *end_lab;
TRACEPB("gen_if", printf("(%p)\n", p));
else_lab = new_clabel();
gen_pp(p -> n_ibool);
gen_bool(p -> n_ibool, FALL_THROUGH, else_lab);
gen_bpost(p -> n_ibool);
gen_list(p -> n_ithen);
/* cut down on generation of spurious nodes since a missing
else clause is quite common */
if (p -> n_ielse) {
g_bra(end_lab = new_clabel());
g_label(else_lab);
gen_bpost(p -> n_ibool);
gen_list(p -> n_ielse);
g_label(end_lab);
}
else {
g_label(else_lab);
}
TICKX("gen_if");
}
/*
Generate code for the for statement.
*/
static void
gen_for(register struct node * p)
{
struct node *lab0, *lab1;
struct node *old_brk, *old_cont;
/* Save old values. */
TRACEPB("gen_for", printf("(%p)\n", p));
old_brk = brk_lab;
old_cont = cont_lab;
lab0 = new_clabel();
lab1 = new_clabel();
brk_lab = new_clabel();
cont_lab = new_clabel();
gen_pp(p -> n_fbool);
if (is_vbool(p -> n_fbool)) {
gen_expr(p -> n_f1list);
g_bra(lab0);
g_label(lab1);
gen_bpost(p -> n_fbool);
gen_list(p -> n_fbdy);
g_label(cont_lab);
gen_expr(p -> n_f2list);
g_label(lab0);
gen_bool(p -> n_fbool, lab1, FALL_THROUGH);
gen_bpost(p -> n_fbool);
g_label(brk_lab);
}
else {
gen_expr(p -> n_f1list);
g_label(lab1);
gen_list(p -> n_fbdy);
g_label(cont_lab);
gen_expr(p -> n_f2list);
g_bra(lab1);
g_label(brk_lab);
}
/* Restore old values. */
brk_lab = old_brk;
cont_lab = old_cont;
TICKX("gen_for");
}
/*
Generate code for the switch statement.
Note: this may end up handling long cases if no fundamental
problems turn up
*/
static void
gen_switch(register struct node * p)
{
struct node * old_brk;
register struct node *q;
register struct node *loc1;
/* Save old value. */
TRACEPB("gen_switch", printf("(%p)\n", p));
old_brk = brk_lab;
/* Generate break label. */
brk_lab = new_clabel();
/* Generate labels for all case statements. */
for (q = p -> n_slist; q; q = q -> n_clist) {
q -> n_clab = new_clabel();
}
/* Generate label for the default case. */
q = p -> n_sdef;
if (q) {
q -> n_clab = new_clabel();
}
/* Generate code to evaluate the switch expression. */
loc1 = gen_dexpr(p -> n_sval); /* not necessarily a dtemp! */
/*
There are a number of possible ways of going to the cases:
1. The obvious chain of compares and branches.
2. A binary tree of compares and branches.
3. A well-balanced binary tree of compares and branches.
4. An index-table jump.
5. A serial-search-table jump.
6. A binary-search-table jump.
*/
/* Generate using method (1). */
for (q = p -> n_slist; q; q = q -> n_clist) {
g_2l2(X_CMP, locn_xconst(q -> n_ccon), loc1);
g_1lab(X_BEQ, q -> n_clab);
}
q = p -> n_sdef;
if (q) {
g_bra(q -> n_clab);
}
else {
g_bra(brk_lab);
}
/* Generate the body. */
gen_list(p -> n_sbdy);
g_label(brk_lab);
/* Restore old break label. */
brk_lab = old_brk;
TICKX("gen_switch");
}
/*
Generate code for the while statement.
*/
static void
gen_while(register struct node * p)
{
struct node *loop_lab;
struct node *old_cont, *old_brk;
/* Save old values. */
TRACEPB("gen_while", printf("(%p)\n", p));
old_brk = brk_lab;
old_cont = cont_lab;
/* Generate new labels. */
loop_lab = new_clabel();
cont_lab = new_clabel();
brk_lab = new_clabel();
gen_pp(p -> n_wbool);
if (is_vbool(p -> n_wbool)) {
g_bra(cont_lab);
gen_bpost(p -> n_wbool);
g_label(loop_lab);
gen_list(p -> n_wbdy);
g_label(cont_lab);
gen_bool(p -> n_wbool, loop_lab, FALL_THROUGH);
gen_bpost(p -> n_wbool);
g_label(brk_lab);
}
else {
g_label(cont_lab);
gen_list(p -> n_wbdy);
g_bra(cont_lab);
}
/* Restore old values. */
brk_lab = old_brk;
cont_lab = old_cont;
TICKX("gen_while");
}
/*
Generate code for the break statement.
*/
static void
gen_break(struct node *p)
{
TRACEPB("gen_break", printf("(%p)\n", p));
g_bra(brk_lab);
TICKX("gen_break");
}
/*
Generate code for the case statement.
*/
static void
gen_case(register struct node * p)
{
TRACEPB("gen_case", printf("(%p)\n", p));
g_label(p -> n_clab);
TICKX("gen_case");
}
/*
Generate code for the continue statement.
*/
static void
gen_continue(struct node *p)
{
TRACEPB("gen_continue", printf("(%p)\n", p));
g_bra(cont_lab);
TICKX("gen_continue");
}
/*
Generate code for the default statement.
*/
static void
gen_default(struct node *p)
{
TRACEPB("gen_default", printf("(%p)\n", p));
g_label(p -> n_clab);
TICKX("gen_default");
}
/*
Generate code for the return statement.
*/
static void
gen_return(register struct node * p)
{
TRACEPB("gen_return", printf("(%p)\n", p));
if (p -> n_rval) {
if (ret_a) {
gen_a0exp(p -> n_rval);
}
else if (ret_d) {
gen_d0exp(p -> n_rval);
}
}
/* Jump to the return label. */
g_bra(ret_lab);
TICKX("gen_return");
}
/*
Generate code for a goto to a user defined label.
*/
static void
gen_goto(struct node * p)
{
TRACEPB("gen_goto", printf("(%p)\n", p));
g_bra(p -> n_plab);
TICKX("gen_goto");
}
/*
Generate a user defined label from a parse label node.
*/
static void
gen_label(struct node *p)
{
TRACEPB("gen_label", printf("(%p)\n", p));
g_label(p -> n_plab);
TICKX("gen_label");
}
/*
Generate a machine-language mnemonic.
NOTE: x_exists() gives the error messages now. This way it can be
more specific and give the line numbers as well.
*/
static void
gen_x(register struct node *p)
{
register struct x_ex *x;
register int type;
TRACEPB("gen_x", printf("(%p)\n", p));
type = p -> n_xtype;
switch(type) {
case X_JMP:
case X_JSR:
if (p -> n_xarg1) {
/* these arguments will have to be generated somehow */
/* the representation isn't clear right now */
if (x_exists(p)) {
g_1(type, p -> n_xarg1);
}
}
else {
g_1lab(type, p -> n_arg2);
}
break;
case X_DBCC: case X_DBCS: case X_DBEQ:
case X_DBF : case X_DBGE: case X_DBGT:
case X_DBHI: case X_DBLE: case X_DBLS:
case X_DBLT: case X_DBMI: case X_DBNE:
case X_DBPL: case X_DBT: case X_DBVC:
case X_DBVS:
if (x_exists(p)) {
g_2lab(type, p -> n_xarg1, p -> n_arg2);
}
break;
case X_BRA: case X_BSR:
case X_BCC: case X_BCS: case X_BEQ:
case X_BGE: case X_BGT: case X_BHI:
case X_BLE: case X_BLS: case X_BLT:
case X_BMI: case X_BNE: case X_BPL:
case X_BVC: case X_BVS:
if (x_exists(p)) {
g_1lab(type, p -> n_arg1);
}
break;
default:
/* verify that arguments are loc_node's */
/* in C, arguments will be addresses of something */
/* that something will be set up in assembler */
gen_pp(p -> n_xarg1);
gen_pp(p -> n_xarg2);
/* See if the specified address modes exist for the function. */
if (x = x_exists(p)) {
g_x(p -> n_xtype, p -> n_xarg1,
p -> n_xarg2, x -> xlen);
}
break;
}
RETURN_VOID("gen_x");
}
/*
Generate a machine-language pseudo operation.
*/
static void
gen_z(register struct node *p)
{
TRACEPB("gen_z", printf("(%p)\n", p));
/* CAUTION: see note in io.c, am_match(), about equates */
switch (p -> n_ztype) {
case Z_BSS: g_lit("BSS"); RETURN_VOID("gen_z");
case Z_DATA: g_lit("DATA"); RETURN_VOID("gen_z");
case Z_TEXT: g_lit("TEXT"); RETURN_VOID("gen_z");
case Z_ORG: case Z_PUSH: case Z_POP:
case Z_ADDSP: case Z_ADJSP: case Z_SUBSP:
p -> n_zarg1 = pe_expr();
RETURN_VOID("gen_z");
case Z_DC: case Z_DCB: case Z_DCW: case Z_DCL:
case Z_DS: case Z_DSB: case Z_DSW: case Z_DSL:
p -> n_zarg1 = pe_list(CONST_EXPR);
RETURN_VOID("gen_z");
default:
fatal("gen_z: unknown type");
}
TICKX("gen_z");
}
/*
Return true if the parse tree that p points to is a variable boolean
expression, i.e., it must be evaluated at run-time to see whether it
is true or false.
*/
bool
is_vbool(register struct node * p)
{
/*
NOT READY YET: see if root is a constant loc_node.
*/
TRACEPB("is_vbool", printf("(%p)\n", p));
if (p == NULL) {
RETURN_BOOL("is_vbool", FALSE);
}
else {
RETURN_BOOL("is_vbool", TRUE);
}
}