ack/lang/cem/cemcom/code.c

654 lines
15 KiB
C
Raw Normal View History

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*/
1986-03-10 13:07:55 +00:00
/* $Header$ */
/* C O D E - G E N E R A T I N G R O U T I N E S */
#include "lint.h"
#ifndef LINT
#include <em.h>
#else
#include "em_lint.h"
#include "l_lint.h"
#endif LINT
#include "botch_free.h"
#include <alloc.h>
1986-03-10 13:07:55 +00:00
#include "dataflow.h"
#include "use_tmp.h"
#include "arith.h"
#include "type.h"
#include "idf.h"
#include "label.h"
#include "code.h"
#include "stmt.h"
1986-03-10 13:07:55 +00:00
#include "def.h"
#include "expr.h"
#include "sizes.h"
#include "stack.h"
#include "level.h"
#include "decspecs.h"
1986-03-19 12:31:05 +00:00
#include "declar.h"
1986-03-10 13:07:55 +00:00
#include "Lpars.h"
#include "specials.h"
#include "atw.h"
#include "assert.h"
1987-03-25 23:14:43 +00:00
#include "noRoption.h"
1988-07-11 12:53:01 +00:00
#include "file_info.h"
label lab_count = 1;
label datlab_count = 1;
1986-03-10 13:07:55 +00:00
#ifndef NOFLOAT
1986-03-10 13:07:55 +00:00
int fp_used;
#endif NOFLOAT
1986-03-10 13:07:55 +00:00
/* global function info */
char *func_name;
struct type *func_type;
int func_notypegiven;
#ifdef USE_TMP
static int tmp_id;
static int pro_id;
#endif USE_TMP
1986-03-10 13:07:55 +00:00
extern char options[];
1988-09-16 23:19:50 +00:00
extern char *symbol2str();
1986-03-10 13:07:55 +00:00
#ifndef LINT
1986-03-10 13:07:55 +00:00
init_code(dst_file)
char *dst_file;
{
/* init_code() initialises the output file on which the
compact EM code is written
*/
C_init(word_size, pointer_size); /* initialise EM module */
1986-03-10 13:07:55 +00:00
if (C_open(dst_file) == 0)
fatal("cannot write to %s\n", dst_file);
C_magic();
C_ms_emx(word_size, pointer_size);
#ifdef USE_TMP
1988-11-16 11:23:58 +00:00
#ifdef PREPEND_SCOPES
C_insertpart(tmp_id = C_getid());
#endif USE_TMP
1988-11-16 11:23:58 +00:00
#endif PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
}
#endif LINT
struct string_cst *str_list = 0;
code_string(val, len, dlb)
char *val;
int len;
label dlb;
{
register struct string_cst *sc = new_string_cst();
C_ina_dlb(dlb);
sc->next = str_list;
str_list = sc;
sc->sc_value = val;
sc->sc_len = len;
sc->sc_dlb = dlb;
}
def_strings(sc)
register struct string_cst *sc;
{
while (sc) {
struct string_cst *sc1 = sc;
C_df_dlb(sc->sc_dlb);
str_cst(sc->sc_value, sc->sc_len);
sc = sc->next;
free_string_cst(sc1);
}
}
#ifndef LINT
1986-03-10 13:07:55 +00:00
end_code()
{
/* end_code() performs the actions to be taken when closing
the output stream.
*/
#ifndef NOFLOAT
if (fp_used) {
/* floating point used */
C_ms_flt();
}
#endif NOFLOAT
def_strings(str_list);
str_list = 0;
1986-08-22 09:20:13 +00:00
C_ms_src((int)(LineNumber - 2), FileName);
1986-03-10 13:07:55 +00:00
C_close();
}
#endif LINT
1986-03-10 13:07:55 +00:00
1988-11-16 11:23:58 +00:00
#ifdef PREPEND_SCOPES
prepend_scopes()
1986-03-10 13:07:55 +00:00
{
/* prepend_scopes() runs down the list of global idf's
and generates those exa's, exp's, ina's and inp's
that superior hindsight has provided.
1986-03-10 13:07:55 +00:00
*/
register struct stack_entry *se = local_level->sl_entry;
1986-03-10 13:07:55 +00:00
1988-11-16 11:23:58 +00:00
#ifdef USE_TMP
C_beginpart(tmp_id);
1988-11-16 11:23:58 +00:00
#endif USE_TMP
1986-03-10 13:07:55 +00:00
while (se != 0) {
register struct idf *id = se->se_idf;
register struct def *df = id->id_def;
1986-03-10 13:07:55 +00:00
if (df && (df->df_initialized || df->df_used || df->df_alloc))
code_scope(id->id_text, df);
1986-03-10 13:07:55 +00:00
se = se->next;
}
1988-11-16 11:23:58 +00:00
#ifdef USE_TMP
C_endpart(tmp_id);
1988-11-16 11:23:58 +00:00
#endif USE_TMP
1986-03-10 13:07:55 +00:00
}
1988-11-16 11:23:58 +00:00
#endif PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
code_scope(text, def)
char *text;
register struct def *def;
1986-03-10 13:07:55 +00:00
{
/* generates code for one name, text, of the storage class
as given by def, if meaningful.
*/
int fund = def->df_type->tp_fund;
switch (def->df_sc) {
case EXTERN:
case GLOBAL:
case IMPLICIT:
if (fund == FUNCTION)
C_exp(text);
else
C_exa_dnam(text);
1986-03-10 13:07:55 +00:00
break;
case STATIC:
if (fund == FUNCTION)
C_inp(text);
else
C_ina_dnam(text);
1986-03-10 13:07:55 +00:00
break;
}
}
1987-08-06 09:55:00 +00:00
static label return_label, return2_label;
static char return_expr_occurred;
static arith func_size;
1986-03-10 13:07:55 +00:00
static label func_res_label;
static char *last_fn_given = "";
static label file_name_label;
begin_proc(ds, idf) /* to be called when entering a procedure */
struct decspecs *ds;
struct idf *idf;
1986-03-10 13:07:55 +00:00
{
/* begin_proc() is called at the entrance of a new function
and performs the necessary code generation:
- a scope indicator (if needed) exp/inp
- the procedure entry pro $name
- reserves some space if the result of the function
does not fit in the return area
- a fil pseudo instruction
*/
register char *name = idf->id_text;
register struct def *def = idf->id_def;
1986-03-10 13:07:55 +00:00
1990-09-12 17:32:31 +00:00
while (def->df_level > L_GLOBAL) def = def->next;
/* idf->id_def does not indicate the right def structure
when the function being defined has a parameter of the
same name.
*/
1988-11-16 11:23:58 +00:00
#ifndef PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
code_scope(name, def);
1988-11-16 11:23:58 +00:00
#endif PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
#ifdef DATAFLOW
if (options['d'])
DfaStartFunction(name);
#endif DATAFLOW
/* set global function info */
func_name = name;
if (def->df_type->tp_fund != FUNCTION) {
error("making function body for non-function");
func_type = error_type;
}
else {
func_type = def->df_type->tp_up;
}
func_notypegiven = ds->ds_notypegiven;
func_size = ATW(func_type->tp_size);
#ifndef USE_TMP
1986-03-10 13:07:55 +00:00
C_pro_narg(name);
#else
C_insertpart(pro_id = C_getid());
#endif
if (is_struct_or_union(func_type->tp_fund)) {
C_df_dlb(func_res_label = data_label());
C_bss_cst(func_size, (arith)0, 1);
1986-03-10 13:07:55 +00:00
}
else
func_res_label = 0;
/* Special arrangements if the function result doesn't fit in
the function return area of the EM machine. The size of
the function return area is implementation dependent.
*/
lab_count = (label) 1;
return_label = text_label();
1987-08-06 09:55:00 +00:00
return2_label = text_label();
return_expr_occurred = 0;
LocalInit();
prc_entry(name);
if (! options['L']) { /* profiling */
1986-03-10 13:07:55 +00:00
if (strcmp(last_fn_given, FileName) != 0) {
/* previous function came from other file */
C_df_dlb(file_name_label = data_label());
C_con_scon(last_fn_given = FileName,
(arith)(strlen(FileName) + 1));
1986-03-10 13:07:55 +00:00
}
/* enable debug trace of EM source */
C_fil_dlb(file_name_label, (arith)0);
1986-03-10 13:07:55 +00:00
C_lin((arith)LineNumber);
}
}
end_proc(fbytes)
arith fbytes;
1986-03-10 13:07:55 +00:00
{
/* end_proc() deals with the code to be generated at the end of
a function, as there is:
- the EM ret instruction: "ret 0"
- loading of the function result in the function
result area if there has been a return <expr>
in the function body (see do_return_expr())
- indication of the use of floating points
- indication of the number of bytes used for
formal parameters
- use of special identifiers such as "setjmp"
- "end" + number of bytes used for local variables
*/
arith nbytes;
1987-07-17 14:30:30 +00:00
char optionsn = options['n'];
1986-03-10 13:07:55 +00:00
#ifdef DATAFLOW
if (options['d'])
DfaEndFunction();
#endif DATAFLOW
1987-08-06 09:55:00 +00:00
C_df_ilb(return2_label);
if (return_expr_occurred) C_asp(-func_size);
C_df_ilb(return_label);
prc_exit();
#ifndef LINT
1987-08-06 09:55:00 +00:00
if (return_expr_occurred) {
if (func_res_label != 0) {
C_lae_dlb(func_res_label, (arith)0);
store_block(func_size, func_type->tp_align);
1987-08-06 09:55:00 +00:00
C_lae_dlb(func_res_label, (arith)0);
C_ret(pointer_size);
}
else
C_ret(func_size);
1986-03-10 13:07:55 +00:00
}
1987-08-06 09:55:00 +00:00
else C_ret((arith) 0);
#endif LINT
/* getting the number of "local" bytes is posponed until here,
because copying the function result in "func_res_label" may
need temporaries! However, local_level is now L_FORMAL2, because
L_LOCAL is already unstacked. Therefore, "unstack_level" must
also pass "sl_max_block" to the level above L_LOCAL.
*/
nbytes = ATW(- local_level->sl_max_block);
#ifdef USE_TMP
C_beginpart(pro_id);
C_pro(func_name, nbytes);
#endif
if (fbytes > max_int) {
error("%s has more than %ld parameter bytes",
func_name, (long) max_int);
}
1986-03-10 13:07:55 +00:00
C_ms_par(fbytes); /* # bytes for formals */
if (sp_occurred[SP_SETJMP]) { /* indicate use of "setjmp" */
1987-07-17 14:30:30 +00:00
options['n'] = 1;
1986-03-10 13:07:55 +00:00
C_ms_gto();
sp_occurred[SP_SETJMP] = 0;
}
#ifdef USE_TMP
C_endpart(pro_id);
#endif
LocalFinish();
C_end(nbytes);
if (nbytes > max_int) {
error("%s has more than %ld bytes of local variables",
func_name, (long) max_int);
}
1987-07-17 14:30:30 +00:00
options['n'] = optionsn;
1986-03-10 13:07:55 +00:00
}
do_return()
{
/* do_return handles the case of a return without expression.
This version branches to the return label, which is
probably smarter than generating a direct return.
Return sequences may be expensive.
*/
1987-08-06 09:55:00 +00:00
C_bra(return2_label);
}
1986-03-10 13:07:55 +00:00
do_return_expr(expr)
struct expr *expr;
{
/* do_return_expr() generates the expression and the jump for
a return statement with an expression.
*/
ch7cast(&expr, RETURN, func_type);
1986-03-10 13:07:55 +00:00
code_expr(expr, RVAL, TRUE, NO_LABEL, NO_LABEL);
C_bra(return_label);
1987-08-06 09:55:00 +00:00
return_expr_occurred = 1;
1986-03-10 13:07:55 +00:00
}
code_declaration(idf, expr, lvl, sc)
register struct idf *idf; /* idf to be declared */
1986-03-10 13:07:55 +00:00
struct expr *expr; /* initialisation; NULL if absent */
int lvl; /* declaration level */
int sc; /* storage class, as in the declaration */
{
/* code_declaration() does the actual declaration of the
variable indicated by "idf" on declaration level "lvl".
If the variable is initialised, the expression is given
1987-03-25 23:14:43 +00:00
in "expr", but for global and static initialisations it
is just non-zero, as the expression is not parsed yet.
1986-03-10 13:07:55 +00:00
There are some cases to be considered:
- filter out typedefs, they don't correspond to code;
- global variables, coded only if initialized;
- local static variables;
- local automatic variables;
Since the expression may be modified in the process,
code_declaration() frees it after use, as the caller can
no longer do so.
1986-03-10 13:07:55 +00:00
If there is a storage class indication (EXTERN/STATIC),
code_declaration() will generate an exa or ina.
The sc is the actual storage class, as given in the
declaration. This is to allow:
extern int a;
int a = 5;
while at the same time forbidding
extern int a = 5;
*/
register struct def *def = idf->id_def;
register arith size = def->df_type->tp_size;
1986-03-10 13:07:55 +00:00
int def_sc = def->df_sc;
if (def_sc == TYPEDEF) /* no code for typedefs */
return;
if (sc == EXTERN && expr && !is_anon_idf(idf))
error("%s is extern; cannot initialize", idf->id_text);
1986-03-10 13:07:55 +00:00
if (lvl == L_GLOBAL) { /* global variable */
/* is this an allocating declaration? */
if ( (sc == 0 || sc == STATIC)
&& def->df_type->tp_fund != FUNCTION
&& size >= 0
)
def->df_alloc = ALLOC_SEEN;
if (expr) { /* code only if initialized */
1988-11-16 11:23:58 +00:00
#ifndef PREPEND_SCOPES
code_scope(idf->id_text, def);
1988-11-16 11:23:58 +00:00
#endif PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
def->df_alloc = ALLOC_DONE;
C_df_dnam(idf->id_text);
1986-03-10 13:07:55 +00:00
}
}
else
if (lvl >= L_LOCAL) { /* local variable */
/* STATIC, EXTERN, GLOBAL, IMPLICIT, AUTO or REGISTER */
1986-03-10 13:07:55 +00:00
switch (def_sc) {
case STATIC:
if (def->df_type->tp_fund == FUNCTION) {
/* should produce "inp $function" ??? */
break;
}
1986-03-10 13:07:55 +00:00
/* they are handled on the spot and get an
integer label in EM.
*/
C_df_dlb((label)def->df_address);
if (expr) { /* there is an initialisation */
}
1986-03-10 13:07:55 +00:00
else { /* produce blank space */
if (size <= 0) {
error("size of %s unknown", idf->id_text);
1986-03-10 13:07:55 +00:00
size = (arith)0;
}
1987-03-20 08:30:31 +00:00
C_bss_cst(ATW(size), (arith)0, 1);
1986-03-10 13:07:55 +00:00
}
break;
case EXTERN:
case GLOBAL:
case IMPLICIT:
/* we are sure there is no expression */
break;
case AUTO:
case REGISTER:
if (expr)
loc_init(expr, idf);
break;
default:
crash("bad local storage class");
1988-09-16 23:19:50 +00:00
/*NOTREACHED*/
1986-03-10 13:07:55 +00:00
}
}
}
loc_init(expr, id)
struct expr *expr;
register struct idf *id;
1986-03-10 13:07:55 +00:00
{
/* loc_init() generates code for the assignment of
expression expr to the local variable described by id.
It frees the expression afterwards.
1986-03-10 13:07:55 +00:00
*/
register struct expr *e = expr;
register struct type *tp = id->id_def->df_type;
1986-03-10 13:07:55 +00:00
ASSERT(id->id_def->df_sc != STATIC);
1986-03-10 13:07:55 +00:00
switch (tp->tp_fund) {
case ARRAY:
case STRUCT:
case UNION:
1988-09-16 23:19:50 +00:00
error("automatic %s cannot be initialized in declaration",
symbol2str(tp->tp_fund));
free_expression(e);
1986-03-10 13:07:55 +00:00
return;
}
if (ISCOMMA(e)) { /* embraced: int i = {12}; */
1987-03-25 23:14:43 +00:00
#ifndef NOROPTION
1986-03-10 13:07:55 +00:00
if (options['R']) {
if (ISCOMMA(e->OP_LEFT)) /* int i = {{1}} */
expr_error(e, "extra braces not allowed");
1986-03-10 13:07:55 +00:00
else
if (e->OP_RIGHT != 0) /* int i = {1 , 2} */
expr_error(e, "too many initializers");
1986-03-10 13:07:55 +00:00
}
1987-03-25 23:14:43 +00:00
#endif NOROPTION
while (e) {
loc_init(e->OP_LEFT, id);
e = e->OP_RIGHT;
1986-03-10 13:07:55 +00:00
}
}
else { /* not embraced */
ch7cast(&expr, '=', tp); /* may modify expr */
#ifndef LINT
1988-08-23 12:45:11 +00:00
{
struct value vl;
EVAL(expr, RVAL, TRUE, NO_LABEL, NO_LABEL);
vl.vl_class = Name;
vl.vl_data.vl_idf = id;
vl.vl_value = (arith)0;
store_val(&vl, tp);
}
#else LINT
id->id_def->df_set = 1;
#endif LINT
1988-08-19 13:55:22 +00:00
free_expression(expr);
1986-03-10 13:07:55 +00:00
}
}
bss(idf)
register struct idf *idf;
1986-03-10 13:07:55 +00:00
{
/* bss() allocates bss space for the global idf.
*/
arith size = idf->id_def->df_type->tp_size;
1986-03-10 13:07:55 +00:00
1988-11-16 11:23:58 +00:00
#ifndef PREPEND_SCOPES
code_scope(idf->id_text, idf->id_def);
1988-11-16 11:23:58 +00:00
#endif PREPEND_SCOPES
1986-03-10 13:07:55 +00:00
/* Since bss() is only called if df_alloc is non-zero, and
since df_alloc is only non-zero if size >= 0, we have:
*/
/* but we already gave a warning at the declaration of the
array. Besides, the message given here does not apply to
voids
1986-03-10 13:07:55 +00:00
if (options['R'] && size == 0)
warning("actual array of size 0");
*/
C_df_dnam(idf->id_text);
1987-03-20 08:30:31 +00:00
C_bss_cst(ATW(size), (arith)0, 1);
1986-03-10 13:07:55 +00:00
}
formal_cvt(df)
register struct def *df;
1986-03-10 13:07:55 +00:00
{
/* formal_cvt() converts a formal parameter of type char or
short from int to that type.
*/
register struct type *tp = df->df_type;
1986-03-10 13:07:55 +00:00
if (tp->tp_size != int_size &&
(tp->tp_fund == CHAR || tp->tp_fund == SHORT)
) {
LoadLocal(df->df_address, int_size);
/* conversion(int_type, df->df_type); ???
No, you can't do this on the stack! (CJ)
*/
StoreLocal(df->df_address, tp->tp_size);
}
1986-03-10 13:07:55 +00:00
}
#ifdef LINT
/*ARGSUSED*/
#endif LINT
1986-03-10 13:07:55 +00:00
code_expr(expr, val, code, tlbl, flbl)
struct expr *expr;
label tlbl, flbl;
{
/* code_expr() is the parser's interface to the expression code
generator. If line number trace is wanted, it generates a
lin instruction. EVAL() is called directly.
*/
#ifndef LINT
if (! options['L']) /* profiling */
1987-04-02 13:02:16 +00:00
C_lin((arith)(expr->ex_line));
1986-03-10 13:07:55 +00:00
EVAL(expr, val, code, tlbl, flbl);
#else LINT
lint_expr(expr, code ? USED : IGNORED);
#endif LINT
1986-03-10 13:07:55 +00:00
}
/* The FOR/WHILE/DO/SWITCH stacking mechanism:
stack_stmt() has to be called at the entrance of a
1986-03-10 13:07:55 +00:00
for, while, do or switch statement to indicate the
EM labels where a subsequent break or continue causes
the program to jump to.
*/
static struct stmt_block *stmt_stack; /* top of statement stack */
/* code_break() generates EM code needed at the occurrence of "break":
1986-03-10 13:07:55 +00:00
it generates a branch instruction to the break label of the
innermost statement in which break has a meaning.
As "break" is legal in any of 'while', 'do', 'for' or 'switch',
which are the only ones that are stacked, only the top of
the stack is interesting.
*/
code_break()
1986-03-10 13:07:55 +00:00
{
register struct stmt_block *stmt_block = stmt_stack;
1986-03-10 13:07:55 +00:00
if (stmt_block)
C_bra(stmt_block->st_break);
else
error("break not inside for, while, do or switch");
1986-03-10 13:07:55 +00:00
}
/* code_continue() generates EM code needed at the occurrence of
"continue":
1986-03-10 13:07:55 +00:00
it generates a branch instruction to the continue label of the
innermost statement in which continue has a meaning.
*/
code_continue()
1986-03-10 13:07:55 +00:00
{
register struct stmt_block *stmt_block = stmt_stack;
1986-03-10 13:07:55 +00:00
while (stmt_block) {
if (stmt_block->st_continue) {
C_bra(stmt_block->st_continue);
return;
1986-03-10 13:07:55 +00:00
}
stmt_block = stmt_block->next;
1986-03-10 13:07:55 +00:00
}
error("continue not inside for, while or do");
1986-03-10 13:07:55 +00:00
}
stack_stmt(break_label, cont_label)
1986-03-10 13:07:55 +00:00
label break_label, cont_label;
{
register struct stmt_block *stmt_block = new_stmt_block();
1986-03-10 13:07:55 +00:00
stmt_block->next = stmt_stack;
stmt_block->st_break = break_label;
stmt_block->st_continue = cont_label;
stmt_stack = stmt_block;
1986-03-10 13:07:55 +00:00
}
unstack_stmt()
1986-03-10 13:07:55 +00:00
{
/* unstack_stmt() unstacks the data of a statement
which may contain break or continue
*/
register struct stmt_block *sbp = stmt_stack;
stmt_stack = sbp->next;
free_stmt_block(sbp);
1986-03-10 13:07:55 +00:00
}
static label prc_name;
prc_entry(name)
char *name;
{
if (options['p']) {
C_df_dlb(prc_name = data_label());
C_rom_scon(name, (arith) (strlen(name) + 1));
C_lae_dlb(prc_name, (arith) 0);
C_cal("procentry");
C_asp(pointer_size);
}
}
prc_exit()
{
if (options['p']) {
C_lae_dlb(prc_name, (arith) 0);
C_cal("procexit");
C_asp(pointer_size);
}
}