BSD 4_3 release
[unix-history] / usr / src / ucb / pascal / src / forop.c
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#ifndef lint
static char sccsid[] = "@(#)forop.c 5.1 (Berkeley) 6/5/85";
#endif not lint
#include "whoami.h"
#include "0.h"
#include "opcode.h"
#include "tree.h"
#include "objfmt.h"
#ifdef PC
# include "pc.h"
# include <pcc.h>
#endif PC
#include "tmps.h"
#include "tree_ty.h"
/*
* for-statements.
*
* the relevant quote from the standard: 6.8.3.9:
* ``The control-variable shall be an entire-variable whose identifier
* is declared in the variable-declaration-part of the block closest-
* containing the for-statement. The control-variable shall possess
* an ordinal-type, and the initial-value and the final-value shall be
* of a type compatible with this type. The statement of a for-statement
* shall not contain an assigning-reference to the control-variable
* of the for-statement. The value of the final-value shall be
* assignment-compatible with the control-variable when the initial-value
* is assigned to the control-variable. After a for-statement is
* executed (other than being left by a goto-statement leading out of it)
* the control-variable shall be undefined. Apart from the restrictions
* imposed by these requirements, the for-statement
* for v := e1 to e2 do body
* shall be equivalent to
* begin
* temp1 := e1;
* temp2 := e2;
* if temp1 <= temp2 then begin
* v := temp1;
* body;
* while v <> temp2 do begin
* v := succ(v);
* body;
* end
* end
* end
* where temp1 and temp2 denote auxiliary variables that the program
* does not otherwise contain, and that possess the type possessed by
* the variable v if that type is not a subrange-type; otherwise the
* host type possessed by the variable v.''
*
* The Berkeley Pascal systems try to do all that without duplicating
* the body, and shadowing the control-variable in (possibly) a
* register variable.
*
* arg here looks like:
* arg[0] T_FORU or T_FORD
* [1] lineof "for"
* [2] [0] T_ASGN
* [1] lineof ":="
* [2] [0] T_VAR
* [1] lineof id
* [2] char * to id
* [3] qualifications
* [3] initial expression
* [3] termination expression
* [4] statement
*/
forop( tree_node)
struct tnode *tree_node;
{
struct tnode *lhs;
VAR_NODE *lhs_node;
FOR_NODE *f_node;
struct nl *forvar;
struct nl *fortype;
#ifdef PC
int forp2type;
#endif PC
int forwidth;
struct tnode *init_node;
struct nl *inittype;
struct nl *initnlp; /* initial value namelist entry */
struct tnode *term_node;
struct nl *termtype;
struct nl *termnlp; /* termination value namelist entry */
struct nl *shadownlp; /* namelist entry for the shadow */
struct tnode *stat_node;
int goc; /* saved gocnt */
int again; /* label at the top of the loop */
int after; /* label after the end of the loop */
struct nl saved_nl; /* saved namelist entry for loop var */
goc = gocnt;
forvar = NLNIL;
if ( tree_node == TR_NIL ) {
goto byebye;
}
f_node = &(tree_node->for_node);
if ( f_node->init_asg == TR_NIL ) {
goto byebye;
}
line = f_node->line_no;
putline();
lhs = f_node->init_asg->asg_node.lhs_var;
init_node = f_node->init_asg->asg_node.rhs_expr;
term_node = f_node->term_expr;
stat_node = f_node->for_stmnt;
if (lhs == TR_NIL) {
nogood:
if (forvar != NIL) {
forvar->value[ NL_FORV ] = FORVAR;
}
(void) rvalue( init_node , NLNIL , RREQ );
(void) rvalue( term_node , NLNIL , RREQ );
statement( stat_node );
goto byebye;
}
else lhs_node = &(lhs->var_node);
/*
* and this marks the variable as used!!!
*/
forvar = lookup( lhs_node->cptr );
if ( forvar == NIL ) {
goto nogood;
}
saved_nl = *forvar;
if ( lhs_node->qual != TR_NIL ) {
error("For variable %s must be unqualified", forvar->symbol);
goto nogood;
}
if (forvar->class == WITHPTR) {
error("For variable %s cannot be an element of a record",
lhs_node->cptr);
goto nogood;
}
if ( opt('s') &&
( ( bn != cbn ) ||
#ifdef OBJ
(whereis(forvar->value[NL_OFFS], 0) == PARAMVAR)
#endif OBJ
#ifdef PC
(whereis(forvar->value[NL_OFFS], forvar->extra_flags)
== PARAMVAR )
#endif PC
) ) {
standard();
error("For variable %s must be declared in the block in which it is used", forvar->symbol);
}
/*
* find out the type of the loop variable
*/
codeoff();
fortype = lvalue( lhs , MOD , RREQ );
codeon();
if ( fortype == NLNIL ) {
goto nogood;
}
if ( isnta( fortype , "bcis" ) ) {
error("For variable %s cannot be %ss", forvar->symbol, nameof( fortype ) );
goto nogood;
}
if ( forvar->value[ NL_FORV ] & FORVAR ) {
error("Can't modify the for variable %s in the range of the loop", forvar->symbol);
forvar = NLNIL;
goto nogood;
}
forwidth = lwidth(fortype);
# ifdef PC
forp2type = p2type(fortype);
# endif PC
/*
* allocate temporaries for the initial and final expressions
* and maybe a register to shadow the for variable.
*/
initnlp = tmpalloc((long) sizeof(long), nl+T4INT, NOREG);
termnlp = tmpalloc((long) sizeof(long), nl+T4INT, NOREG);
shadownlp = tmpalloc((long) forwidth, fortype, REGOK);
# ifdef PC
/*
* compute and save the initial expression
*/
putRV((char *) 0 , cbn , initnlp -> value[ NL_OFFS ] ,
initnlp -> extra_flags , PCCT_INT );
# endif PC
# ifdef OBJ
(void) put(2, O_LV | cbn<<8+INDX, initnlp -> value[ NL_OFFS ] );
# endif OBJ
inittype = rvalue( init_node , fortype , RREQ );
if ( incompat( inittype , fortype , init_node ) ) {
cerror("Type of initial expression clashed with index type in 'for' statement");
if (forvar != NLNIL) {
forvar->value[ NL_FORV ] = FORVAR;
}
(void) rvalue( term_node , NLNIL , RREQ );
statement( stat_node );
goto byebye;
}
# ifdef PC
sconv(p2type(inittype), PCCT_INT);
putop( PCC_ASSIGN , PCCT_INT );
putdot( filename , line );
/*
* compute and save the termination expression
*/
putRV((char *) 0 , cbn , termnlp -> value[ NL_OFFS ] ,
termnlp -> extra_flags , PCCT_INT );
# endif PC
# ifdef OBJ
(void) gen(O_AS2, O_AS2, sizeof(long), width(inittype));
/*
* compute and save the termination expression
*/
(void) put(2, O_LV | cbn<<8+INDX, termnlp -> value[ NL_OFFS ] );
# endif OBJ
termtype = rvalue( term_node , fortype , RREQ );
if ( incompat( termtype , fortype , term_node ) ) {
cerror("Type of limit expression clashed with index type in 'for' statement");
if (forvar != NLNIL) {
forvar->value[ NL_FORV ] = FORVAR;
}
statement( stat_node );
goto byebye;
}
# ifdef PC
sconv(p2type(termtype), PCCT_INT);
putop( PCC_ASSIGN , PCCT_INT );
putdot( filename , line );
/*
* we can skip the loop altogether if !( init <= term )
*/
after = (int) getlab();
putRV((char *) 0 , cbn , initnlp -> value[ NL_OFFS ] ,
initnlp -> extra_flags , PCCT_INT );
putRV((char *) 0 , cbn , termnlp -> value[ NL_OFFS ] ,
termnlp -> extra_flags , PCCT_INT );
putop( ( tree_node->tag == T_FORU ? PCC_LE : PCC_GE ) , PCCT_INT );
putleaf( PCC_ICON , after , 0 , PCCT_INT, (char *) 0 );
putop( PCC_CBRANCH , PCCT_INT );
putdot( filename , line );
/*
* okay, so we have to execute the loop body,
* but first, if checking is on,
* check that the termination expression
* is assignment compatible with the control-variable.
*/
if (opt('t')) {
precheck(fortype, "_RANG4", "_RSNG4");
putRV((char *) 0, cbn, termnlp -> value[NL_OFFS],
termnlp -> extra_flags, PCCT_INT);
postcheck(fortype, nl+T4INT);
putdot(filename, line);
}
/*
* assign the initial expression to the shadow
* checking the assignment if necessary.
*/
putRV((char *) 0, cbn, shadownlp -> value[NL_OFFS],
shadownlp -> extra_flags, forp2type);
if (opt('t')) {
precheck(fortype, "_RANG4", "_RSNG4");
putRV((char *) 0, cbn, initnlp -> value[NL_OFFS],
initnlp -> extra_flags, PCCT_INT);
postcheck(fortype, nl+T4INT);
} else {
putRV((char *) 0, cbn, initnlp -> value[NL_OFFS],
initnlp -> extra_flags, PCCT_INT);
}
sconv(PCCT_INT, forp2type);
putop(PCC_ASSIGN, forp2type);
putdot(filename, line);
/*
* put down the label at the top of the loop
*/
again = (int) getlab();
(void) putlab((char *) again );
/*
* each time through the loop
* assign the shadow to the for variable.
*/
(void) lvalue(lhs, NOUSE, RREQ);
putRV((char *) 0, cbn, shadownlp -> value[NL_OFFS],
shadownlp -> extra_flags, forp2type);
putop(PCC_ASSIGN, forp2type);
putdot(filename, line);
# endif PC
# ifdef OBJ
(void) gen(O_AS2, O_AS2, sizeof(long), width(termtype));
/*
* we can skip the loop altogether if !( init <= term )
*/
(void) put(2, O_RV4 | cbn<<8+INDX, initnlp -> value[ NL_OFFS ] );
(void) put(2, O_RV4 | cbn<<8+INDX, termnlp -> value[ NL_OFFS ] );
(void) gen(NIL, tree_node->tag == T_FORU ? T_LE : T_GE, sizeof(long),
sizeof(long));
after = (int) getlab();
(void) put(2, O_IF, after);
/*
* okay, so we have to execute the loop body,
* but first, if checking is on,
* check that the termination expression
* is assignment compatible with the control-variable.
*/
if (opt('t')) {
(void) put(2, O_LV | cbn<<8+INDX, shadownlp -> value[ NL_OFFS ] );
(void) put(2, O_RV4 | cbn<<8+INDX, termnlp -> value[ NL_OFFS ] );
rangechk(fortype, nl+T4INT);
(void) gen(O_AS2, O_AS2, forwidth, sizeof(long));
}
/*
* assign the initial expression to the shadow
* checking the assignment if necessary.
*/
(void) put(2, O_LV | cbn<<8+INDX, shadownlp -> value[ NL_OFFS ] );
(void) put(2, O_RV4 | cbn<<8+INDX, initnlp -> value[ NL_OFFS ] );
rangechk(fortype, nl+T4INT);
(void) gen(O_AS2, O_AS2, forwidth, sizeof(long));
/*
* put down the label at the top of the loop
*/
again = (int) getlab();
(void) putlab( (char *) again );
/*
* each time through the loop
* assign the shadow to the for variable.
*/
(void) lvalue(lhs, NOUSE, RREQ);
(void) stackRV(shadownlp);
(void) gen(O_AS2, O_AS2, forwidth, sizeof(long));
# endif OBJ
/*
* shadowing the real for variable
* with the shadow temporary:
* save the real for variable flags (including nl_block).
* replace them with the shadow's offset,
* and mark the for variable as being a for variable.
*/
shadownlp -> nl_flags |= NLFLAGS(forvar -> nl_flags);
*forvar = *shadownlp;
forvar -> symbol = saved_nl.symbol;
forvar -> nl_next = saved_nl.nl_next;
forvar -> type = saved_nl.type;
forvar -> value[ NL_FORV ] = FORVAR;
/*
* and don't forget ...
*/
putcnt();
statement( stat_node );
/*
* wasn't that fun? do we get to do it again?
* we don't do it again if ( !( forvar < limit ) )
* pretend we were doing this at the top of the loop
*/
line = f_node->line_no;
# ifdef PC
if ( opt( 'p' ) ) {
if ( opt('t') ) {
putleaf( PCC_ICON , 0 , 0 , PCCM_ADDTYPE( PCCTM_FTN | PCCT_INT , PCCTM_PTR )
, "_LINO" );
putop( PCCOM_UNARY PCC_CALL , PCCT_INT );
putdot( filename , line );
} else {
putRV( STMTCOUNT , 0 , 0 , NGLOBAL , PCCT_INT );
putleaf( PCC_ICON , 1 , 0 , PCCT_INT , (char *) 0 );
putop( PCCOM_ASG PCC_PLUS , PCCT_INT );
putdot( filename , line );
}
}
/*rvalue( lhs_node , NIL , RREQ );*/
putRV( (char *) 0 , cbn , shadownlp -> value[ NL_OFFS ] ,
shadownlp -> extra_flags , forp2type );
sconv(forp2type, PCCT_INT);
putRV( (char *) 0 , cbn , termnlp -> value[ NL_OFFS ] ,
termnlp -> extra_flags , PCCT_INT );
putop( ( tree_node->tag == T_FORU ? PCC_LT : PCC_GT ) , PCCT_INT );
putleaf( PCC_ICON , after , 0 , PCCT_INT , (char *) 0 );
putop( PCC_CBRANCH , PCCT_INT );
putdot( filename , line );
/*
* okay, so we have to do it again,
* but first, increment the for variable.
* no need to rangecheck it, since we checked the
* termination value before we started.
*/
/*lvalue( lhs , MOD , RREQ );*/
putRV( (char *) 0 , cbn , shadownlp -> value[ NL_OFFS ] ,
shadownlp -> extra_flags , forp2type );
/*rvalue( lhs_node , NIL , RREQ );*/
putRV( (char *) 0 , cbn , shadownlp -> value[ NL_OFFS ] ,
shadownlp -> extra_flags , forp2type );
sconv(forp2type, PCCT_INT);
putleaf( PCC_ICON , 1 , 0 , PCCT_INT , (char *) 0 );
putop( ( tree_node->tag == T_FORU ? PCC_PLUS : PCC_MINUS ) , PCCT_INT );
sconv(PCCT_INT, forp2type);
putop( PCC_ASSIGN , forp2type );
putdot( filename , line );
/*
* and do it all again
*/
putjbr( (long) again );
/*
* and here we are
*/
(void) putlab( (char *) after );
# endif PC
# ifdef OBJ
/*
* okay, so we have to do it again.
* Luckily we have a magic opcode which increments the
* index variable, checks the limit falling through if
* it has been reached, else updating the index variable,
* and returning to the top of the loop.
*/
putline();
(void) put(2, O_RV4 | cbn<<8+INDX, termnlp -> value[ NL_OFFS ] );
(void) put(2, O_LV | cbn<<8+INDX, shadownlp -> value[ NL_OFFS ] );
(void) put(2, (tree_node->tag == T_FORU ? O_FOR1U : O_FOR1D) + (forwidth >> 1),
again);
/*
* and here we are
*/
patch( (PTR_DCL) after );
# endif OBJ
byebye:
noreach = FALSE;
if (forvar != NLNIL) {
saved_nl.nl_flags |= NLFLAGS(forvar -> nl_flags) & (NUSED|NMOD);
*forvar = saved_nl;
}
if ( goc != gocnt ) {
putcnt();
}
}