1193 lines
25 KiB
C
1193 lines
25 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*
|
|
* Author: Ceriel J.H. Jacobs
|
|
*/
|
|
|
|
/* P A R S E T R E E W A L K E R */
|
|
|
|
/* $Id$ */
|
|
|
|
/* Routines to walk through parts of the parse tree, and generate
|
|
code for these parts.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "parameters.h"
|
|
#include "debug.h"
|
|
|
|
#include <em_arith.h>
|
|
#include <em_label.h>
|
|
#include <em_reg.h>
|
|
#include <em_code.h>
|
|
#include <m2_traps.h>
|
|
#include <assert.h>
|
|
#include <alloc.h>
|
|
#include <stb.h>
|
|
|
|
#include "LLlex.h"
|
|
#include "def.h"
|
|
#include "type.h"
|
|
#include "scope.h"
|
|
#include "main.h"
|
|
#include "node.h"
|
|
#include "Lpars.h"
|
|
#include "desig.h"
|
|
#include "typequiv.h"
|
|
#include "f_info.h"
|
|
#include "idf.h"
|
|
#include "chk_expr.h"
|
|
#include "walk.h"
|
|
#include "misc.h"
|
|
#include "error.h"
|
|
#include "tmpvar.h"
|
|
#include "stab.h"
|
|
#include "code.h"
|
|
#include "warning.h"
|
|
|
|
int CaseCode(struct node *, label, int);
|
|
|
|
extern int proclevel;
|
|
extern int gdb_flag;
|
|
|
|
label text_label;
|
|
label data_label = 1;
|
|
struct withdesig* WithDesigs;
|
|
struct node* Modules;
|
|
|
|
static struct type* func_type;
|
|
static struct node* priority;
|
|
static int oldlineno;
|
|
|
|
|
|
|
|
|
|
#define NO_EXIT_LABEL ((label)0)
|
|
#define RETURN_LABEL ((label)1)
|
|
|
|
#define REACH_FLAG 1
|
|
#define EXIT_FLAG 2
|
|
|
|
/* Forward declarations. */
|
|
static void WalkDef(register struct def*);
|
|
static void MkCalls(register struct def*);
|
|
static void UseWarnings(register struct def*);
|
|
static void RegisterMessage(register struct def*);
|
|
static void WalkDefList(register struct def*, void (*proc)(struct def*));
|
|
#ifdef DBSYMTAB
|
|
static void stabdef(struct def*);
|
|
#endif
|
|
|
|
|
|
int LblWalkNode(label lbl, struct node *nd, int exit, int reach)
|
|
{
|
|
/* Generate code for node "nd", after generating instruction
|
|
label "lbl". "exit" is the exit label for the closest
|
|
enclosing LOOP.
|
|
*/
|
|
|
|
def_ilb(lbl);
|
|
return WalkNode(nd, exit, reach);
|
|
}
|
|
|
|
static arith tmpprio;
|
|
|
|
static void DoPriority(void)
|
|
{
|
|
/* For the time being (???), handle priorities by calls to
|
|
the runtime system
|
|
*/
|
|
if (priority)
|
|
{
|
|
tmpprio = NewInt();
|
|
C_loc(priority->nd_INT);
|
|
CAL("stackprio", (int)word_size);
|
|
C_lfr(word_size);
|
|
C_stl(tmpprio);
|
|
}
|
|
}
|
|
|
|
static void EndPriority(void)
|
|
{
|
|
if (priority)
|
|
{
|
|
C_lol(tmpprio);
|
|
CAL("unstackprio", (int)word_size);
|
|
FreeInt(tmpprio);
|
|
}
|
|
}
|
|
|
|
void def_ilb(label l)
|
|
{
|
|
/* Instruction label definition. Forget about line number.
|
|
*/
|
|
C_df_ilb(l);
|
|
oldlineno = 0;
|
|
}
|
|
|
|
void DoLineno(register struct node* nd)
|
|
{
|
|
if ((!options['L']
|
|
#ifdef DBSYMTAB
|
|
|| options['g']
|
|
#endif /* DBSYMTAB */
|
|
)
|
|
&& nd->nd_lineno && nd->nd_lineno != oldlineno)
|
|
{
|
|
oldlineno = nd->nd_lineno;
|
|
if (!options['L'])
|
|
C_lin((arith)nd->nd_lineno);
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
static int ms_lineno;
|
|
|
|
if (ms_lineno != nd->nd_lineno)
|
|
{
|
|
ms_lineno = nd->nd_lineno;
|
|
C_ms_std((char*)0, N_SLINE, ms_lineno);
|
|
}
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
}
|
|
}
|
|
|
|
void DoFilename(int needed)
|
|
{
|
|
static label filename_label = 0;
|
|
|
|
oldlineno = 0; /* always invalidate remembered line number */
|
|
if (needed && !options['L'])
|
|
{
|
|
|
|
if (!filename_label)
|
|
{
|
|
filename_label = 1;
|
|
C_df_dlb((label)1);
|
|
C_rom_scon(FileName, (arith)(strlen(FileName) + 1));
|
|
}
|
|
|
|
C_fil_dlb((label)1, (arith)0);
|
|
}
|
|
}
|
|
|
|
void WalkModule(register struct def* module)
|
|
{
|
|
register struct scope* sc;
|
|
struct scopelist* savevis = CurrVis;
|
|
|
|
CurrVis = module->mod_vis;
|
|
priority = module->mod_priority;
|
|
sc = CurrentScope;
|
|
|
|
/* Walk through it's local definitions
|
|
*/
|
|
WalkDefList(sc->sc_def, WalkDef);
|
|
|
|
/* Now, generate initialization code for this module.
|
|
First call initialization routines for modules defined within
|
|
this module.
|
|
*/
|
|
sc->sc_off = 0; /* no locals (yet) */
|
|
text_label = 1; /* label at end of initialization routine */
|
|
TmpOpen(sc); /* Initialize for temporaries */
|
|
C_pro_narg(sc->sc_name);
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
stb_string(module, D_MODULE);
|
|
WalkDefList(sc->sc_def, stabdef);
|
|
if (state == PROGRAM && module == Defined)
|
|
{
|
|
C_ms_stb_cst(module->df_idf->id_text,
|
|
N_MAIN,
|
|
0,
|
|
(arith)0);
|
|
}
|
|
stb_string(module, D_END);
|
|
}
|
|
#endif
|
|
DoPriority();
|
|
if (module == Defined)
|
|
{
|
|
/* Body of implementation or program module.
|
|
Call initialization routines of imported modules.
|
|
Also prevent recursive calls of this one.
|
|
*/
|
|
register struct node* nd = Modules;
|
|
|
|
if (state == IMPLEMENTATION)
|
|
{
|
|
/* We don't actually prevent recursive calls,
|
|
but do nothing if called recursively
|
|
*/
|
|
C_df_dlb(++data_label);
|
|
C_con_cst((arith)0);
|
|
/* if this one is set to non-zero, the initialization
|
|
was already done.
|
|
*/
|
|
C_loe_dlb(data_label, (arith)0);
|
|
C_zne(RETURN_LABEL);
|
|
C_ine_dlb(data_label, (arith)0);
|
|
}
|
|
else if (!options['R'])
|
|
{
|
|
/* put funny value in BSS, in an attempt to detect
|
|
uninitialized variables
|
|
*/
|
|
C_cal("killbss");
|
|
}
|
|
|
|
for (; nd; nd = nd->nd_NEXT)
|
|
{
|
|
C_cal(nd->nd_def->mod_vis->sc_scope->sc_name);
|
|
}
|
|
DoFilename(1);
|
|
}
|
|
WalkDefList(sc->sc_def, MkCalls);
|
|
proclevel++;
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
C_ms_std((char*)0, N_LBRAC, gdb_flag ? 0 : proclevel);
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
WalkNode(module->mod_body, NO_EXIT_LABEL, REACH_FLAG);
|
|
DO_DEBUG(options['X'], PrNode(module->mod_body, 0));
|
|
def_ilb(RETURN_LABEL);
|
|
EndPriority();
|
|
C_ret((arith)0);
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
C_ms_std((char*)0, N_RBRAC, gdb_flag ? 0 : proclevel);
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
C_end(-sc->sc_off);
|
|
proclevel--;
|
|
TmpClose();
|
|
|
|
CurrVis = savevis;
|
|
WalkDefList(sc->sc_def, UseWarnings);
|
|
}
|
|
|
|
void WalkProcedure(register struct def* procedure)
|
|
{
|
|
|
|
struct scopelist* savevis = CurrVis;
|
|
register struct type* tp;
|
|
register struct paramlist* param;
|
|
register struct scope* procscope = procedure->prc_vis->sc_scope;
|
|
label too_big = 0; /* returnsize larger than returnarea */
|
|
arith StackAdjustment = 0; /* space for conformant arrays */
|
|
arith retsav = 0; /* temporary space for return value */
|
|
arith func_res_size = 0;
|
|
#ifdef USE_INSERT
|
|
int partno = C_getid();
|
|
int partno2 = C_getid();
|
|
#else
|
|
label cd_init;
|
|
label cd_body;
|
|
#endif
|
|
|
|
proclevel++;
|
|
CurrVis = procedure->prc_vis;
|
|
|
|
/* Generate code for all local modules and procedures
|
|
*/
|
|
WalkDefList(procscope->sc_def, WalkDef);
|
|
|
|
func_type = tp = RemoveEqual(ResultType(procedure->df_type));
|
|
|
|
if (tp)
|
|
{
|
|
func_res_size = WA(tp->tp_size);
|
|
if (TooBigForReturnArea(tp))
|
|
{
|
|
#ifdef BIG_RESULT_ON_STACK
|
|
/* The result type of this procedure is too big.
|
|
The caller will have reserved space on its stack,
|
|
above the parameters, to store the result.
|
|
*/
|
|
too_big = 1;
|
|
#else
|
|
/* The result type of this procedure is too big.
|
|
The actual procedure will return a pointer to a
|
|
global data area in which the function result is
|
|
stored.
|
|
Notice that this makes the code non-reentrant.
|
|
Here, we create the data area for the function
|
|
result.
|
|
*/
|
|
too_big = ++data_label;
|
|
C_df_dlb(too_big);
|
|
C_bss_cst(func_res_size, (arith)0, 0);
|
|
#endif /* BIG_RESULT_ON_STACK */
|
|
}
|
|
}
|
|
|
|
/* Generate code for this procedure
|
|
*/
|
|
TmpOpen(procscope);
|
|
#ifdef USE_INSERT
|
|
C_insertpart(partno2); /* procedure header */
|
|
#else
|
|
C_pro_narg(procedure->prc_name);
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
stb_string(procedure, D_PROCEDURE);
|
|
WalkDefList(procscope->sc_def, stabdef);
|
|
stb_string(procedure, D_PEND);
|
|
C_ms_std((char*)0, N_LBRAC, gdb_flag ? 0 : proclevel);
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
C_ms_par(procedure->df_type->prc_nbpar
|
|
#ifdef BIG_RESULT_ON_STACK
|
|
+ (too_big ? func_res_size : 0)
|
|
#endif
|
|
);
|
|
#endif
|
|
/* generate code for filename only when the procedure can be
|
|
exported, either directly or by taking the address.
|
|
This cannot be done if the level is bigger than one (because in
|
|
this case it is a nested procedure).
|
|
*/
|
|
DoFilename(procscope->sc_level == 1);
|
|
DoPriority();
|
|
|
|
text_label = 1; /* label at end of procedure */
|
|
|
|
/* Check if we must save the stack pointer */
|
|
for (param = ParamList(procedure->df_type);
|
|
param;
|
|
param = param->par_next)
|
|
{
|
|
if (!IsVarParam(param))
|
|
{
|
|
tp = TypeOfParam(param);
|
|
|
|
if (IsConformantArray(tp))
|
|
{
|
|
/* First time we get here
|
|
*/
|
|
if (func_type && !too_big)
|
|
{
|
|
/* Some local space, only
|
|
needed if the value itself
|
|
is returned
|
|
*/
|
|
retsav = TmpSpace(func_res_size, 1);
|
|
}
|
|
StackAdjustment = NewPtr();
|
|
C_lor((arith)1);
|
|
STL(StackAdjustment, pointer_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USE_INSERT
|
|
C_insertpart(partno);
|
|
#else
|
|
cd_init = ++text_label;
|
|
cd_body = ++text_label;
|
|
c_bra(cd_init);
|
|
def_ilb(cd_body);
|
|
#endif
|
|
|
|
if ((WalkNode(procedure->prc_body, NO_EXIT_LABEL, REACH_FLAG) & REACH_FLAG))
|
|
{
|
|
if (func_res_size)
|
|
{
|
|
node_warning(procscope->sc_end,
|
|
W_ORDINARY,
|
|
"function procedure \"%s\" does not always return a value",
|
|
procedure->df_idf->id_text);
|
|
c_loc(M2_NORESULT);
|
|
C_trp();
|
|
C_asp(-func_res_size);
|
|
}
|
|
#ifndef USE_INSERT
|
|
c_bra(RETURN_LABEL);
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_INSERT
|
|
C_beginpart(partno);
|
|
#else
|
|
def_ilb(cd_init);
|
|
#endif
|
|
|
|
/* Generate calls to initialization routines of modules defined within
|
|
this procedure
|
|
*/
|
|
WalkDefList(procscope->sc_def, MkCalls);
|
|
|
|
/* Make sure that arguments of size < word_size are on a
|
|
fixed place.
|
|
Also make copies of parameters when neccessary.
|
|
*/
|
|
for (param = ParamList(procedure->df_type);
|
|
param;
|
|
param = param->par_next)
|
|
{
|
|
if (!IsVarParam(param))
|
|
{
|
|
tp = TypeOfParam(param);
|
|
|
|
if (!IsConformantArray(tp))
|
|
{
|
|
if (tp->tp_size < word_size && (int)word_size % (int)tp->tp_size == 0)
|
|
{
|
|
C_lol(param->par_def->var_off);
|
|
STL(param->par_def->var_off,
|
|
tp->tp_size);
|
|
}
|
|
continue;
|
|
}
|
|
/* Here, we have to make a copy of the
|
|
array. We must also remember how much
|
|
room is reserved for copies, because
|
|
we have to adjust the stack pointer before
|
|
a RET is done. This is even more complicated
|
|
when the procedure returns a value.
|
|
Then, the value must be saved,
|
|
the stack adjusted, the return value pushed
|
|
again, and then RET
|
|
*/
|
|
/* First compute new stackpointer */
|
|
C_lal(param->par_def->var_off);
|
|
CAL("new_stackptr", (int)pointer_size);
|
|
C_lfr(pointer_size);
|
|
C_ass(pointer_size);
|
|
/* adjusted stack pointer */
|
|
LOL(param->par_def->var_off, pointer_size);
|
|
/* push source address */
|
|
CAL("copy_array", (int)pointer_size);
|
|
/* copy */
|
|
}
|
|
}
|
|
#ifdef USE_INSERT
|
|
C_endpart(partno);
|
|
#else
|
|
c_bra(cd_body);
|
|
#endif
|
|
DO_DEBUG(options['X'], PrNode(procedure->prc_body, 0));
|
|
def_ilb(RETURN_LABEL); /* label at end */
|
|
if (too_big)
|
|
{
|
|
/* Fill the data area reserved for the function result
|
|
with the result
|
|
*/
|
|
#ifdef BIG_RESULT_ON_STACK
|
|
C_lal(procedure->df_type->prc_nbpar);
|
|
#else
|
|
c_lae_dlb(too_big);
|
|
#endif /* BIG_RESULT_ON_STACK */
|
|
C_sti(func_res_size);
|
|
if (StackAdjustment)
|
|
{
|
|
/* Remove copies of conformant arrays
|
|
*/
|
|
LOL(StackAdjustment, pointer_size);
|
|
C_str((arith)1);
|
|
}
|
|
#ifdef BIG_RESULT_ON_STACK
|
|
func_res_size = 0;
|
|
#else
|
|
c_lae_dlb(too_big);
|
|
func_res_size = pointer_size;
|
|
#endif /* BIG_RESULT_ON_STACK */
|
|
}
|
|
else if (StackAdjustment)
|
|
{
|
|
/* First save the function result in a safe place.
|
|
Then remove copies of conformant arrays,
|
|
and put function result back on the stack
|
|
*/
|
|
if (func_type)
|
|
{
|
|
STL(retsav, func_res_size);
|
|
}
|
|
LOL(StackAdjustment, pointer_size);
|
|
C_str((arith)1);
|
|
if (func_type)
|
|
{
|
|
LOL(retsav, func_res_size);
|
|
}
|
|
}
|
|
EndPriority();
|
|
C_ret(func_res_size);
|
|
#ifdef USE_INSERT
|
|
C_beginpart(partno2);
|
|
C_pro(procedure->prc_name, -procscope->sc_off);
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
stb_string(procedure, D_PROCEDURE);
|
|
WalkDefList(procscope->sc_def, stabdef);
|
|
stb_string(procedure, D_PEND);
|
|
C_ms_std((char*)0, N_LBRAC, gdb_flag ? 0 : proclevel);
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
C_ms_par(procedure->df_type->prc_nbpar
|
|
#ifdef BIG_RESULT_ON_STACK
|
|
+ (too_big ? func_res_size : 0)
|
|
#endif
|
|
);
|
|
#endif
|
|
if (!options['n'])
|
|
WalkDefList(procscope->sc_def, RegisterMessage);
|
|
#ifdef USE_INSERT
|
|
C_endpart(partno2);
|
|
#endif
|
|
#ifdef DBSYMTAB
|
|
if (options['g'])
|
|
{
|
|
C_ms_std((char*)0, N_RBRAC, gdb_flag ? 0 : proclevel);
|
|
}
|
|
#endif /* DBSYMTAB */
|
|
C_end(-procscope->sc_off);
|
|
if (!fit(procscope->sc_off, (int)word_size))
|
|
{
|
|
node_error(procedure->prc_body,
|
|
"maximum local byte count exceeded");
|
|
}
|
|
TmpClose();
|
|
CurrVis = savevis;
|
|
proclevel--;
|
|
WalkDefList(procscope->sc_def, UseWarnings);
|
|
}
|
|
|
|
/* Walk through a list of definitions */
|
|
static void WalkDef(register struct def* df)
|
|
{
|
|
|
|
|
|
switch (df->df_kind)
|
|
{
|
|
case D_MODULE:
|
|
WalkModule(df);
|
|
break;
|
|
case D_PROCEDURE:
|
|
WalkProcedure(df);
|
|
break;
|
|
case D_VARIABLE:
|
|
if (!proclevel && !(df->df_flags & D_ADDRGIVEN))
|
|
{
|
|
C_df_dnam(df->var_name);
|
|
C_bss_cst(
|
|
WA(df->df_type->tp_size),
|
|
(arith)0, 0);
|
|
}
|
|
break;
|
|
default:
|
|
/* nothing */
|
|
;
|
|
}
|
|
}
|
|
|
|
/* Generate calls to initialization routines of modules */
|
|
static void MkCalls(register struct def* df)
|
|
{
|
|
|
|
|
|
if (df->df_kind == D_MODULE)
|
|
{
|
|
C_lxl((arith)0);
|
|
CAL(df->mod_vis->sc_scope->sc_name, (int)pointer_size);
|
|
}
|
|
}
|
|
|
|
int WalkLink(register struct node* nd, label exit_label, int end_reached)
|
|
{
|
|
|
|
while (nd && nd->nd_class == Link)
|
|
{ /* statement list */
|
|
end_reached = WalkNode(nd->nd_LEFT, exit_label, end_reached);
|
|
nd = nd->nd_RIGHT;
|
|
}
|
|
|
|
return WalkNode(nd, exit_label, end_reached);
|
|
}
|
|
|
|
static void ForLoopVarExpr(register struct node* nd)
|
|
{
|
|
register struct type* tp = nd->nd_type;
|
|
|
|
CodePExpr(nd);
|
|
CodeCoercion(tp, BaseType(tp));
|
|
}
|
|
|
|
int WalkStat(register struct node* nd, label exit_label, int end_reached)
|
|
{
|
|
|
|
register struct node* left = nd->nd_LEFT;
|
|
register struct node* right = nd->nd_RIGHT;
|
|
|
|
assert(nd->nd_class == Stat);
|
|
|
|
if (nd->nd_symb == ';')
|
|
return 1;
|
|
|
|
if (!end_reached & REACH_FLAG)
|
|
{
|
|
node_warning(nd, W_ORDINARY, "statement not reached");
|
|
}
|
|
if (nd->nd_symb != WHILE || nd->nd_lineno != left->nd_lineno)
|
|
{
|
|
/* Avoid double linenumber generation in while statements */
|
|
DoLineno(nd);
|
|
}
|
|
options['R'] = (nd->nd_flags & ROPTION);
|
|
options['A'] = (nd->nd_flags & AOPTION);
|
|
switch (nd->nd_symb)
|
|
{
|
|
case '(':
|
|
{
|
|
struct node* nd1 = nd;
|
|
if (ChkCall(&nd1))
|
|
{
|
|
assert(nd == nd1);
|
|
if (nd->nd_type != 0)
|
|
{
|
|
node_error(nd, "only proper procedures can be called from top-level "
|
|
"statement; this is a function procedure");
|
|
break;
|
|
}
|
|
CodeCall(nd);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BECOMES:
|
|
DoAssign(nd);
|
|
break;
|
|
|
|
case IF:
|
|
{
|
|
label l1 = ++text_label, l3 = ++text_label;
|
|
int end_r;
|
|
|
|
ExpectBool(&(nd->nd_LEFT), l3, l1);
|
|
assert(right->nd_symb == THEN);
|
|
end_r = LblWalkNode(l3, right->nd_LEFT, exit_label, end_reached);
|
|
|
|
if (right->nd_RIGHT)
|
|
{ /* ELSE part */
|
|
label l2 = ++text_label;
|
|
|
|
c_bra(l2);
|
|
end_reached = end_r | LblWalkNode(l1, right->nd_RIGHT, exit_label, end_reached);
|
|
l1 = l2;
|
|
}
|
|
else
|
|
end_reached |= end_r;
|
|
def_ilb(l1);
|
|
break;
|
|
}
|
|
|
|
case CASE:
|
|
end_reached = CaseCode(nd, exit_label, end_reached);
|
|
break;
|
|
|
|
case WHILE:
|
|
{
|
|
label loop = ++text_label,
|
|
exit = ++text_label,
|
|
dummy = ++text_label;
|
|
|
|
c_bra(dummy);
|
|
end_reached |= LblWalkNode(loop, right, exit_label, end_reached);
|
|
def_ilb(dummy);
|
|
ExpectBool(&(nd->nd_LEFT), loop, exit);
|
|
def_ilb(exit);
|
|
break;
|
|
}
|
|
|
|
case REPEAT:
|
|
{
|
|
label loop = ++text_label, exit = ++text_label;
|
|
|
|
end_reached = LblWalkNode(loop, left, exit_label, end_reached);
|
|
ExpectBool(&(nd->nd_RIGHT), exit, loop);
|
|
def_ilb(exit);
|
|
break;
|
|
}
|
|
|
|
case LOOP:
|
|
{
|
|
label loop = ++text_label, exit = ++text_label;
|
|
|
|
if (LblWalkNode(loop, right, exit, end_reached) & EXIT_FLAG)
|
|
{
|
|
end_reached &= REACH_FLAG;
|
|
}
|
|
else
|
|
end_reached = 0;
|
|
c_bra(loop);
|
|
def_ilb(exit);
|
|
break;
|
|
}
|
|
|
|
case FOR:
|
|
{
|
|
arith tmp = NewInt();
|
|
arith tmp2 = NewInt();
|
|
int good_forvar;
|
|
label l1 = ++text_label;
|
|
label l2 = ++text_label;
|
|
int uns = 0;
|
|
arith stepsize;
|
|
struct type* bstp;
|
|
struct node* loopid;
|
|
|
|
good_forvar = DoForInit(left);
|
|
loopid = left->nd_LEFT;
|
|
if ((stepsize = right->nd_LEFT->nd_INT) == 0)
|
|
{
|
|
node_warning(right->nd_LEFT,
|
|
W_ORDINARY,
|
|
"zero stepsize in FOR loop");
|
|
}
|
|
if (good_forvar)
|
|
{
|
|
bstp = BaseType(loopid->nd_type);
|
|
uns = bstp->tp_fund != T_INTEGER;
|
|
CodePExpr(left->nd_RIGHT->nd_RIGHT);
|
|
C_stl(tmp);
|
|
CodePExpr(left->nd_RIGHT->nd_LEFT);
|
|
C_dup(int_size);
|
|
C_stl(tmp2);
|
|
C_lol(tmp);
|
|
if (uns)
|
|
C_cmu(int_size);
|
|
else
|
|
C_cmi(int_size);
|
|
if (stepsize >= 0)
|
|
C_zgt(l2);
|
|
else
|
|
C_zlt(l2);
|
|
C_lol(tmp2);
|
|
RangeCheck(loopid->nd_type,
|
|
left->nd_RIGHT->nd_LEFT->nd_type);
|
|
CodeDStore(loopid);
|
|
if (stepsize >= 0)
|
|
{
|
|
C_lol(tmp);
|
|
ForLoopVarExpr(loopid);
|
|
}
|
|
else
|
|
{
|
|
stepsize = -stepsize;
|
|
ForLoopVarExpr(loopid);
|
|
C_lol(tmp);
|
|
}
|
|
C_sbu(int_size);
|
|
if (stepsize)
|
|
{
|
|
C_loc(stepsize);
|
|
C_dvu(int_size);
|
|
}
|
|
C_stl(tmp);
|
|
loopid->nd_def->df_flags |= D_FORLOOP;
|
|
def_ilb(l1);
|
|
if (!options['R'])
|
|
{
|
|
ForLoopVarExpr(loopid);
|
|
C_stl(tmp2);
|
|
}
|
|
end_reached |= WalkNode(right->nd_RIGHT, exit_label, end_reached);
|
|
if (!options['R'])
|
|
{
|
|
label x = ++text_label;
|
|
C_lol(tmp2);
|
|
ForLoopVarExpr(loopid);
|
|
C_beq(x);
|
|
c_loc(M2_FORCH);
|
|
C_trp();
|
|
def_ilb(x);
|
|
}
|
|
loopid->nd_def->df_flags &= ~D_FORLOOP;
|
|
FreeInt(tmp2);
|
|
if (stepsize)
|
|
{
|
|
C_lol(tmp);
|
|
C_zeq(l2);
|
|
C_lol(tmp);
|
|
c_loc(1);
|
|
C_sbu(int_size);
|
|
C_stl(tmp);
|
|
C_loc(right->nd_LEFT->nd_INT);
|
|
ForLoopVarExpr(loopid);
|
|
C_adu(int_size);
|
|
RangeCheck(loopid->nd_type, bstp);
|
|
CodeDStore(loopid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
end_reached |= WalkNode(right->nd_RIGHT, exit_label, end_reached);
|
|
loopid->nd_def->df_flags &= ~D_FORLOOP;
|
|
}
|
|
c_bra(l1);
|
|
def_ilb(l2);
|
|
FreeInt(tmp);
|
|
}
|
|
break;
|
|
|
|
case WITH:
|
|
{
|
|
struct scopelist link;
|
|
struct withdesig wds;
|
|
struct desig ds;
|
|
|
|
if (!WalkDesignator(&(nd->nd_LEFT), &ds, D_USED))
|
|
break;
|
|
left = nd->nd_LEFT;
|
|
if (left->nd_type->tp_fund != T_RECORD)
|
|
{
|
|
node_error(left, "record variable expected");
|
|
break;
|
|
}
|
|
|
|
wds.w_next = WithDesigs;
|
|
wds.w_flags = D_USED;
|
|
WithDesigs = &wds;
|
|
wds.w_scope = left->nd_type->rec_scope;
|
|
CodeAddress(&ds);
|
|
ds.dsg_kind = DSG_FIXED;
|
|
/* Create a designator structure for the temporary.
|
|
*/
|
|
ds.dsg_offset = NewPtr();
|
|
ds.dsg_name = 0;
|
|
CodeStore(&ds, address_type);
|
|
ds.dsg_kind = DSG_PFIXED;
|
|
/* the record is indirectly available */
|
|
wds.w_desig = ds;
|
|
link.sc_scope = wds.w_scope;
|
|
link.sc_next = CurrVis;
|
|
CurrVis = &link;
|
|
end_reached = WalkNode(right, exit_label, end_reached);
|
|
CurrVis = link.sc_next;
|
|
WithDesigs = wds.w_next;
|
|
FreePtr(ds.dsg_offset);
|
|
ChkDesig(&(nd->nd_LEFT), wds.w_flags & (D_USED | D_DEFINED));
|
|
break;
|
|
}
|
|
|
|
case EXIT:
|
|
assert(exit_label != 0);
|
|
|
|
if (end_reached & REACH_FLAG)
|
|
end_reached = EXIT_FLAG;
|
|
c_bra(exit_label);
|
|
break;
|
|
|
|
case RETURN:
|
|
end_reached &= ~REACH_FLAG;
|
|
if (right)
|
|
{
|
|
if (!ChkExpression(&(nd->nd_RIGHT)))
|
|
break;
|
|
/* The type of the return-expression must be
|
|
assignment compatible with the result type of the
|
|
function procedure (See Rep. 9.11).
|
|
*/
|
|
if (!ChkAssCompat(&(nd->nd_RIGHT), func_type, "RETURN"))
|
|
{
|
|
break;
|
|
}
|
|
right = nd->nd_RIGHT;
|
|
if (right->nd_type->tp_fund == T_STRING)
|
|
{
|
|
CodePString(right, func_type);
|
|
}
|
|
else
|
|
CodePExpr(right);
|
|
}
|
|
c_bra(RETURN_LABEL);
|
|
break;
|
|
|
|
default:
|
|
crash("(WalkStat)");
|
|
}
|
|
return end_reached;
|
|
}
|
|
|
|
|
|
int (*WalkTable[])(struct node*, label, int) = {
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
NodeCrash,
|
|
WalkStat,
|
|
NodeCrash,
|
|
WalkLink,
|
|
};
|
|
|
|
extern struct desig null_desig;
|
|
|
|
void ExpectBool(register struct node** pnd, label true_label, label false_label)
|
|
{
|
|
|
|
struct desig ds;
|
|
|
|
ds = null_desig;
|
|
if (ChkExpression(pnd))
|
|
{
|
|
if ((*pnd)->nd_type != bool_type && (*pnd)->nd_type != error_type)
|
|
{
|
|
node_error(*pnd, "boolean expression expected");
|
|
}
|
|
|
|
CodeExpr(*pnd, &ds, true_label, false_label);
|
|
}
|
|
}
|
|
|
|
int WalkDesignator(struct node** pnd, struct desig* ds, int flags)
|
|
{
|
|
|
|
if (!ChkVariable(pnd, flags))
|
|
return 0;
|
|
|
|
*ds = null_desig;
|
|
CodeDesig(*pnd, ds);
|
|
return 1;
|
|
}
|
|
|
|
int DoForInit(struct node* nd)
|
|
{
|
|
register struct node* right = nd->nd_RIGHT;
|
|
register struct def* df;
|
|
struct type* base_tp;
|
|
struct type *tpl, *tpr;
|
|
int r;
|
|
|
|
r = ChkVariable(&(nd->nd_LEFT), D_USED | D_DEFINED);
|
|
r &= ChkExpression(&(right->nd_LEFT));
|
|
r &= ChkExpression(&(right->nd_RIGHT));
|
|
if (!r)
|
|
return 0;
|
|
|
|
df = nd->nd_LEFT->nd_def;
|
|
if (df->df_kind == D_FIELD)
|
|
{
|
|
node_error(nd,
|
|
"FOR-loop variable may not be a field of a record");
|
|
return 1;
|
|
}
|
|
|
|
if (!df->var_name && df->var_off >= 0)
|
|
{
|
|
node_error(nd, "FOR-loop variable may not be a parameter");
|
|
return 1;
|
|
}
|
|
|
|
if (df->df_scope != CurrentScope)
|
|
{
|
|
register struct scopelist* sc = CurrVis;
|
|
|
|
for (;;)
|
|
{
|
|
if (!sc)
|
|
{
|
|
node_error(nd,
|
|
"FOR-loop variable may not be imported");
|
|
return 1;
|
|
}
|
|
if (sc->sc_scope == df->df_scope)
|
|
break;
|
|
sc = nextvisible(sc);
|
|
}
|
|
}
|
|
|
|
if (df->df_type->tp_size > word_size || !(df->df_type->tp_fund & T_DISCRETE))
|
|
{
|
|
node_error(nd, "illegal type of FOR loop variable");
|
|
return 1;
|
|
}
|
|
|
|
base_tp = BaseType(df->df_type);
|
|
tpl = right->nd_LEFT->nd_type;
|
|
tpr = right->nd_RIGHT->nd_type;
|
|
#ifndef STRICT_3RD_ED
|
|
if (!options['3'])
|
|
{
|
|
if (!ChkAssCompat(&(right->nd_LEFT), base_tp, "FOR statement") || !ChkAssCompat(&(right->nd_RIGHT), base_tp, "FOR statement"))
|
|
{
|
|
return 1;
|
|
}
|
|
if (!TstCompat(df->df_type, tpl) || !TstCompat(df->df_type, tpr))
|
|
{
|
|
node_warning(nd, W_OLDFASHIONED, "compatibility required in FOR statement");
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (!ChkCompat(&(right->nd_LEFT), base_tp, "FOR statement") || !ChkCompat(&(right->nd_RIGHT), base_tp, "FOR statement"))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void DoAssign(register struct node* nd)
|
|
{
|
|
/* May we do it in this order (expression first) ???
|
|
The reference manual sais nothing about it, but the book does:
|
|
it sais that the left hand side is evaluated first.
|
|
DAMN THE BOOK!
|
|
*/
|
|
struct desig dsr;
|
|
register struct type* tp;
|
|
|
|
if (!(ChkExpression(&(nd->nd_RIGHT)) & ChkVariable(&(nd->nd_LEFT), D_DEFINED)))
|
|
return;
|
|
tp = nd->nd_LEFT->nd_type;
|
|
|
|
if (!ChkAssCompat(&(nd->nd_RIGHT), tp, "assignment"))
|
|
{
|
|
return;
|
|
}
|
|
dsr = null_desig;
|
|
|
|
#define StackNeededFor(ds) ((ds).dsg_kind == DSG_PLOADED \
|
|
|| (ds).dsg_kind == DSG_INDEXED)
|
|
CodeExpr(nd->nd_RIGHT, &dsr, NO_LABEL, NO_LABEL);
|
|
tp = nd->nd_RIGHT->nd_type;
|
|
if (complex(tp))
|
|
{
|
|
if (StackNeededFor(dsr))
|
|
CodeAddress(&dsr);
|
|
}
|
|
else
|
|
{
|
|
CodeValue(&dsr, tp);
|
|
}
|
|
CodeMove(&dsr, nd->nd_LEFT, tp);
|
|
}
|
|
|
|
static void RegisterMessage(register struct def* df)
|
|
{
|
|
register struct type* tp;
|
|
|
|
if (df->df_kind == D_VARIABLE)
|
|
{
|
|
if (!(df->df_flags & D_NOREG))
|
|
{
|
|
/* Examine type and size
|
|
*/
|
|
tp = BaseType(df->df_type);
|
|
if ((df->df_flags & D_VARPAR) || (tp->tp_fund & (T_POINTER | T_HIDDEN | T_EQUAL)))
|
|
{
|
|
C_ms_reg(df->var_off,
|
|
pointer_size,
|
|
reg_pointer,
|
|
0);
|
|
}
|
|
else if (tp->tp_fund & T_NUMERIC)
|
|
{
|
|
C_ms_reg(df->var_off,
|
|
tp->tp_size,
|
|
tp->tp_fund == T_REAL ? reg_float : reg_any,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void df_warning(struct node* nd, struct def* df, char* warning)
|
|
{
|
|
if (!(df->df_kind & (D_VARIABLE | D_PROCEDURE | D_TYPE | D_CONST | D_PROCHEAD)))
|
|
{
|
|
return;
|
|
}
|
|
if (warning)
|
|
{
|
|
node_warning(nd,
|
|
W_ORDINARY,
|
|
"%s \"%s\" %s",
|
|
(df->df_flags & D_VALPAR) ? "value parameter" : (df->df_flags & D_VARPAR) ? "variable parameter" : (df->df_kind == D_VARIABLE) ? "variable" : (df->df_kind == D_TYPE) ? "type" : (df->df_kind == D_CONST) ? "constant" : "procedure",
|
|
df->df_idf->id_text, warning);
|
|
}
|
|
}
|
|
|
|
static void UseWarnings(register struct def* df)
|
|
{
|
|
struct node* nd = df->df_scope->sc_end;
|
|
|
|
if (is_anon_idf(df->df_idf) || !(df->df_kind & (D_IMPORTED | D_VARIABLE | D_PROCEDURE | D_CONST | D_TYPE)) || (df->df_flags & (D_EXPORTED | D_QEXPORTED)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (df->df_kind & D_IMPORTED)
|
|
{
|
|
register struct def* df1 = df->imp_def;
|
|
|
|
df1->df_flags |= df->df_flags & (D_USED | D_DEFINED);
|
|
if (df->df_kind == D_INUSE)
|
|
return;
|
|
if (!(df->df_flags & D_IMP_BY_EXP))
|
|
{
|
|
if (df->df_flags & (D_USED | D_DEFINED))
|
|
{
|
|
return;
|
|
}
|
|
df_warning(nd,
|
|
df1,
|
|
df1->df_kind == D_VARIABLE ? "imported but not used/assigned" : "imported but not used");
|
|
return;
|
|
}
|
|
df = df1;
|
|
nd = df->df_scope->sc_end;
|
|
}
|
|
switch (df->df_flags & (D_USED | D_DEFINED | D_VALPAR | D_VARPAR))
|
|
{
|
|
case 0:
|
|
case D_VARPAR:
|
|
df_warning(nd, df, "never used/assigned");
|
|
break;
|
|
case D_USED:
|
|
df_warning(nd, df, "never assigned");
|
|
break;
|
|
case D_VALPAR:
|
|
case D_DEFINED:
|
|
case D_DEFINED | D_VALPAR:
|
|
df_warning(nd, df, "never used");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void WalkDefList(register struct def* df, void (*proc)(struct def*))
|
|
{
|
|
for (; df; df = df->df_nextinscope)
|
|
{
|
|
(*proc)(df);
|
|
}
|
|
}
|
|
|
|
#ifdef DBSYMTAB
|
|
static void stabdef(struct def* df)
|
|
{
|
|
switch (df->df_kind)
|
|
{
|
|
case D_CONST:
|
|
case D_VARIABLE:
|
|
stb_string(df, df->df_kind);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|