! File: FLOWAN.BLI
!
Module FLOWAN=
Begin
! FLOWAN MODULE
! -------------
!
! THE FUNCTION OF THIS MODULE IS TO PERFORM GLOBAL FLOW ANALYSIS.
! IT PERFORMS COMMON-SUB-EXPRESSION RECOGNITION AND FINDS FEASIBLE
! CODE MOTION OPTIMIZATIONS.
!
!
Require 'Bliss';
Global
FLOOR : LVL;
Own
hdr_alpha : Ref GT,
hdr_omega : Ref GT,
hdr_psi : Ref GT,
hdr_chi : Ref GT,
hdr_rho : Ref GT,
FOUNDATION : Ref Vector,
CEILING : LVL,
LVLCOPY : LVL,
hdr_bogus : LVL,
hdr_prolog : LVL,
hdr_mu : LVL,
hdr_postlog : LVL,
hdr_epilog : LVL,
hdr_kill : Ref ITEM,
ABCBASE : LVL;
External Routine
DELETETOMARK : Novalue;
Forward Routine
ABCBETW,
BINDPCSTHREAD : Novalue,
ENTVCHGLST : Novalue,
ENTVUSELST : Novalue,
GCSEFROMPSI : Novalue,
GENPRLG : Novalue,
GENPSI : Novalue,
SEARCHFORKILLS,
WISCHUSED;
! PUSHLV and POPLV implement variable length stacks
! for LVLCOPY, FLOOR, CEILING, ABCBASE, hdr_prolog, etc.
Macro
PUSHLV(L,N) =
Begin
Bind Q = L : LVL;
Local ZQ14 : Ref LVL;
ZQ14 = GETSPACE(N);
ZQ14[stk_next] = .Q[stk_next];
ZQ14[stk_data] = .Q[stk_data];
Q[stk_next] = .ZQ14
End %,
POPLV(L,N) =
Begin
Bind Q = L : LVL;
Local ZQ13 : Ref LVL;
ZQ13 = .Q[stk_next];
Q[stk_next] = .ZQ13[stk_next];
Q[stk_data] = .ZQ13[stk_data];
RELEASESPACE(.ZQ13,N)
End %,
FORALLRATORS(I) =
Decr I From MAXOPERATOR To 0 Do %,
FORALLRANDS(I,J) =
Decr I From J[gt_argc]-1 To 0 Do %;
! GLOBAL FLOW ANALYSIS ROUTINES
! ------------------------------
!
! CALLED BY DOMODULE
! PERFORMS INITIALIZATION OF DATA USED BY FLOWAN
!
Global Routine FLOWINIT : Novalue =
Begin
FLOOR[stk_data] = 0;
FLOOR[stk_next] = 0;
FOUNDATION = 0;
LEVEL = 1;
LEVELINC = 1;
CEILING[stk_data] = 1;
CEILING[stk_next] = 0;
LVLCOPY[stk_data] = 1;
LVLCOPY[stk_next] = 0;
ABCOUNT = 1;
ABCBASE[stk_data] = 1;
ABCBASE[stk_next] = 0;
hdr_bogus[stk_data] = MAKHDR(BOGREMOVE,BOGENTER);
hdr_prolog[stk_data] = MAKHDR(PRLGREMOVE,PRLGENTER);
hdr_kill = MAKHDR(KILREMOVE,KILENTER);
CLEARCORE(GTHASH,MAXDELIMITER+2)
End;
! CALLED FROM: ENRHO, NONBOGUS (RECURSIVE), FIND NAME, MARK DOT NODES,
! MARK UP, MARK ALL, GALOMBITS, OMEG DECR, OMEGHEADECR, F11.
!
! ARGUMENT: A GT NODE
!
! VALUE: A GT NODE FORMALLY IDENTICAL TO THE FIRST, BUT WHICH IS
! NOT A 'BOGUS' NODE.
!
! PURPOSE: 'BOGUS' NODES HAVE NO OPERANDS; THEREFORE, ANY ROUTINE
! WHICH NEEDS TO SEE THE OPERANDS OF A NODE MUST (USUALLY)
! CALL THIS ROUTINE.
!
! notes:
! bogus nodes are created by GCSEFROMPSI. They are much
! like alpha/omega lists in characteristic. they represent
! CSE's which are common to all branches of a forked expressions
! (IF and CASE). these CSE's are still valid outside the
! forked expression but may not be moved outside it due to
! essential ordering constraints.
!
! a bogus node represents a 'potential' CSE. if it is
! never referenced outside the fork then it behaves as if
! nothing is common between the forks.
Global Routine NONBOGUS(NODE : Ref GT) =
Begin
! loop while bogus (can a bogus point to a bogus?)
While .NODE[gt_v_bogus] Do
! if the bogus node has had its potential CSE list bound
If .NODE[gt_csthread] Neqa 0 Then
NODE = .NODE[gt_csthread]
Else
! the potential CSE list has not been bound
NODE = .NODE[gt_pcsthread];
Return .NODE
End;
! PUSHES LISTS FLOOR OR CEILING AND SIMULTANEOUSLY LEVEL
Global Routine PUSHANDBUMP(Z : Ref LVL) : Novalue =
Begin
LVLCOPY[stk_data] = .LEVEL;
PUSHLV(LVLCOPY,SZ_LVL);
PUSHLV(.Z,SZ_LVL);
LEVEL = .LEVEL + .LEVELINC;
Z[stk_data] = .LEVEL
End;
! POPS LIST FLOOR OR CEILING AND SIMULTANEOUSLY LEVEL
Global Routine POPANDDUMP(Z : Ref LVL) : Novalue =
Begin
POPLV(.Z,SZ_LVL);
POPLV(LVLCOPY,SZ_LVL);
LEVEL = .LVLCOPY[stk_data]
End;
! CALLED BY: SLABEL, SFLABEL (IN SYNTAX)
! ARGUMENT: SYMBOL TABLE ENTRY FOR A LABEL
! CALLED WHEN: SYNTAX PROCESSING FOR THE LABELED EXPRESSION
! IS ABOUT TO BEGIN
Global Routine NOTELEVEL(S : Ref ST) : Novalue =
Begin
S[st_lab_inc] = .LEVELINC;
S[st_lab_level] = .LEVEL;
LEVELINC = .LEVELINC * 2
End;
! CALLED BY: F24
! ARGUMENTS: A STACK (EITHER LVLCOPY OR CEILING) 'STACK'
! THE INFORMATION SAVED BY NOTELEVEL IN SOME LABEL ('LABLEVEL','INC')
! CALLED WHEN: THE FIRST 'LEAVE' TO SOME LABEL IS ENCOUNTERED
! PURPOSE: FOLLOW DOWN THE STACK INCREMENTING VALUES BY 'INC'
! UNTIL A VALUE LESS THAN 'LABLEVEL' IS FOUND
Routine NOTELEAVE(STACK : Ref LVL,LABLEVEL : Integer,INC : Integer) : Novalue =
Begin
Local
s : Ref LVL;
! when a LEAVE/EXITLOOP occurs, any CSE's discovered after the LEAVE/EXITLOOP
! must NOT be allowed to be placed in the alpha or omega lists. This is
! because the CSE's may not be reached and thus not valid. The invalidation
! is performed by raising level and ceiling between the level of the
! label and the current level. The raised level will cause those CSE's
! to be purged.
S = .STACK;
Until .S Eqla 0 Or .S[stk_data] Lss .LABLEVEL Do
Begin
S[stk_data] = .S[stk_data]+.INC;
S = .S[stk_next]
End
End;
! CALLED FROM: F1, F8, F10, F12
! CALLED WHEN: ON ENTRY TO EACH LINEAR BLOCK.
! PURPOSE: CREATE NEW PROLOG LIST & SAVE OLD; DITTO WITH ABCBASE
Routine PUSHFLO : Novalue =
Begin
PUSHLV(ABCBASE,SZ_LVL);
ABCBASE[stk_data] = .ABCOUNT;
ABCOUNT = .ABCOUNT + 1;
PUSHLV(hdr_prolog,SZ_LVL);
hdr_prolog[stk_data] = MAKHDR(PRLGREMOVE,PRLGENTER)
End;
! CALLED FROM: F4, F13, F16, F17, F18
! CALLED WHEN: ON EXIT FROM EACH LINEAR BLOCK.
! PURPOSE: POP WHAT PUSHFLO PUSHED.
Routine POPFLO : Novalue =
Begin
POPLV(hdr_prolog,SZ_LVL);
POPLV(ABCBASE,SZ_LVL)
End;
! CALLED FROM: FIND NAME (RECURSIVE), MARK DOT NODES, GALOMBITS,
! ENTVUSELST, ENTVCHGLST, WISCHUSED, F11.
! ARGUMENT: A LEXEME
! VALUE: IF THE LEXEME "LOOKS LIKE" AN UNDOTTED SYMBOL TABLE ENTRY,
! A POINTER TO THE SYMBOL TABLE ENTRY; OTHERWISE 0.
! NOTE THAT IF THE LEXEME IS THE SYMBOL TABLE ENTRY FOR "A+4",
! A POINTER TO THE STE FOR "A" IS RETURNED.
Routine FINDNAME(LEX : Ref GT) =
Begin
Local
L1 : Ref GT,
L2 : Ref GT;
! Q: how can a non-address type symbol get here?
If .LEX[gt_type] Eql T_VARIABLE Then
LEX = .LEX[gt_disp];
If .LEX[gt_type] Eql T_SYMBOL Then
If .LEX[st_code] Leq HIGHADDTYPE And .LEX[st_v_namexp] Then
Return .LEX[st_var_base]
Else
Return .LEX;
! if a literal
If .LEX[gt_type] Neq T_NODE Then
Return 0;
L1 = .LEX;
Selectone .L1[gt_code] Of Set
[OP_STORE]:
! see if it looks like it is taking the address of a name
Return FINDNAME(.L1[gt_arg2]); ! RHS of '='
[OP_POINTER]:
Return FINDNAME(.L1[gt_arg1]);
[OP_IF]:
! if both sides of a fork are the same name then return that name
!
! Q: why not do OP_CASE also in the same fashion?
Begin
L2 = FINDNAME(.L1[gt_arg3]);
If .L2 Eql FINDNAME(.L1[gt_arg4]) Then
Return .L2
Else
Return 0
End;
[OP_COMPOUND]:
! compound statements are transparent
Return FINDNAME(.L1[gt_argv(.L1[gt_argc]-1)]);
[OP_FPARM]:
! see if it looks like it is passing the address of a name in a call
Return FINDNAME(.L1[gt_arg1]);
[OP_DOT]:
Return 0
Tes;
If .L1[gt_code] Gtr MAXOPERATOR Then
Return 0;
! for operators, examine all the operands for a name.
!
! Q: what sense does it make to examine something like a '*'
! operator for a name? only '+' and '-' seem to make sense
! and could be bogus in cases like 'A = .Max(B,C)'.
L1 = NONBOGUS(.L1);
FORALLRANDS(I,.L1)
Begin
L2 = FINDNAME(.L1[gt_argv(.I)]);
If .L2 Neqa 0 Then
Return .L2
End;
Return 0
End;
Forward Routine
MARKALL : Novalue;
! CALLED FROM: MARK DOT NODES, MARK ALL, F11
! PURPOSE: SET 'MUST MARK' BIT OF A NODE, AND ADJUST ITS MARK LEVEL.
Macro MRK(L)=
Begin
! if not purged
If Not .L[gt_v_purge] And Not .L[gt_v_rm] Then
! if already marked then note the highest level
If .L[gt_v_mm] Then
Begin
If .L[gt_mklevel] Gtr .LEVEL Then
L[gt_mklevel] = .LEVEL
End
! first time marked. note the level
Else
Begin
L[gt_v_mm] = TRUE;
L[gt_mklevel] = .LEVEL
End
End %;
! CALLED FROM: FIND NO POI (RECURSIVE), FIND ANY OCCUR, MARK DOT NODES
! PURPOSE: GIVEN E
, RETURNS E
! GIVEN ANY OTHER LEXEME, RETURNS LEXEME ITSELF
Routine FINDNOPOI(NODE : Ref GT) =
Begin
While .NODE[gt_type] Eql T_NODE And .NODE[gt_code] Eql OP_POINTER Do
NODE = .NODE[gt_arg1];
Return .NODE
End;
! CALLED FROM: MARK DOT NODES
! FUNCTION:
! PREDICATE INDICATING THAT L AND LEX ARE "APPROXIMATELY"
! FORMALLY IDENTICAL. "APPROXIMATE" MEANS THAT WE MAY FIRST
! HAVE TO STRIP
OFF OF LEX.
!
! note: 'L' has already had a 'FINDNOPOI' done to it.
Routine FINDANYOCCUR(L : Ref GT,NODE : Ref GT) =
Begin
If .NODE Eql .L Then
Return TRUE;
NODE = FINDNOPOI(.NODE);
If .NODE[gt_type] Neq T_NODE Then
Return FALSE;
If .L[gt_fparent] Eql .NODE[gt_fparent] Then
Return TRUE;
Return FALSE
End;
!
! CALLED FROM: GENGT (IN SYNTAX), F11
! PURPOSE:
! IF 'X = ' OCCURS, OR X APPEARS UNDOTTED AS A ROUTINE CALL
! PARAMETER, MARK ALL '.X' NODES.
!
Global Routine MRKDOTNODES(LEX : Ref GT) : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
M : Ref GT,
Q : Ref ST,
LLEX : Ref GT;
! if quick then CSE's are not recognized.
If .swit_quick Then
Return;
! see if the dot of a name
L = .GTHASH[OP_DOT];
LLEX = FINDNAME(.LEX);
! if not a dotted variable
If .LLEX Eqla 0 Then
Begin
! if we should set the must mark of everything because of parnoid mode.
If .swit_mark Then
Begin
MARKALL(FALSE);
Return
End;
! if a dotted literal
LEX = FINDNOPOI(.LEX);
If .LEX[gt_type] Eql T_LITERAL Then
! search for dotted references to the same literal and set must marks
While .L Neq 0 Do
Begin
M = NONBOGUS(.L);
If FINDNOPOI(.M[gt_arg1]) Eql .LEX Then
Begin
LFP = .L[gt_fparent];
Do
MRK(LFP)
While (LFP = .LFP[gt_fsthread]) Neq 0
End;
L = .L[gt_gthread]
End
Else
! search for dotted references to similar expressions
While .L Neq 0 Do
Begin
M = NONBOGUS(.L);
If FINDANYOCCUR(.LEX,.M[gt_arg1]) Then
Begin
LFP = .L[gt_fparent];
Do
MRK(LFP)
While (LFP = .LFP[gt_fsthread]) Neq 0
End;
L = .L[gt_gthread]
End;
Return
End;
! a dotted variable. look for dotted variables similar to this
! and expressions when this is not a register and the mark flag
! is set.
Do
Begin
While .L Neq 0 Do
Begin
M = NONBOGUS(.L);
Q = FINDNAME(.M[gt_arg1]);
If .Q Eql .LLEX Then
Begin
LFP = .L;
Exitloop
End;
If .Q Eqla 0 Then
If .swit_mark And (.LLEX[st_code] Neq S_REGISTER) Then
Begin
LFP = .L;
Exitloop
End;
L = .L[gt_gthread]
End;
If .L Eql 0 Then
Return;
Do
MRK(LFP)
While (LFP = .LFP[gt_fsthread]) Neq 0
End
While (L = .L[gt_gthread]) Neq 0
End;
! PROPAGATES THE MARK-BITS UP FROM L'S DESCENDANTS TO L.
! CALLED FROM MARKMMNODES.
Routine MARKUP(NODE : Ref GT) =
Begin
Local
MARKED : Boolean,
Q : Ref GT,
L : Ref GT;
! only expressions may be marked
If .NODE[gt_type] Neq T_NODE Then
Return FALSE;
! return if already marked
L = .NODE[gt_csparent];
If .L[gt_v_rm] Then
Return TRUE;
! if a must-mark node then mark it and return. the mark level was
! noted when the MM bit was set
Q = NONBOGUS(.L);
If .L[gt_v_mm] Then
Begin
Q[gt_v_rm] = TRUE;
Q[gt_v_mm] = FALSE;
L[gt_v_rm] = TRUE;
L[gt_v_mm] = FALSE;
Return TRUE
End;
! try to mark any children
MARKED = FALSE;
FORALLRANDS(I,.Q)
MARKED = .MARKED Or MARKUP(.Q[gt_argv(.I)]);
! if any children were marked then mark this one as well
! and also note the level it was marked at
If .MARKED Then
Begin
If Not .L[gt_v_purge] Then
Q[gt_mklevel] = L[gt_mklevel] = .LEVEL;
Q[gt_v_rm] = TRUE;
L[gt_v_rm] = TRUE
End;
Return .MARKED
End;
! CALLED FROM: F4, F5, F7, F9, F15, F17, F19, F23, SCOMPOUND
! CALLED WHEN: SIDE EFFECTS MUST BE ACCOUNTED FOR, E.G. AT
! EVERY SEMICOLON IN A COMPOUND STATEMENT
! PURPOSE: SET 'REAL MARK' BITS IN ALL NODES WHOSE 'MUST MARK' BITS ARE ON.
!
! notes:
! see page 39 of "The Design of an Optimizing Compiler".
Global Routine MARKMMNODES : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT;
ABCOUNT = .ABCOUNT + 1;
! if no optimize then just mark everything
If Not .swit_optimize Then
MARKALL(TRUE);
! return if no CSE recognition
If .swit_quick Then
Return;
! loop for each CSE parent, setting the real mark of those with
! the must mark set
FORALLRATORS(I)
Begin
LFP = .GTHASH[.I];
While .LFP Neqa 0 Do
Begin
L = .LFP;
Do
MARKUP(.L)
While (L = .L[gt_fsthread]) Neqa 0;
LFP = .LFP[gt_gthread]
End
End
End;
!
! CALLED FROM MARK DOT NODES, MARK MM NODES, F3, F21
! MARK ALL NODES ON THE DOT CHAIN.
! IF 'MRKREGS' ISN'T SET, DON'T MARK '.R' IF R IS A REGISTER VARIABLE.
!
Routine MARKALL(MRKREGS : Boolean) : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
M : Ref GT;
! return if no CSE recognition
If .swit_quick Then
Return;
! loop for each CSE parent, setting the must mark of everything but
! dotted registers (unless we want dotted registers as well)
LFP = .GTHASH[OP_DOT];
While .LFP Neqa 0 Do
Begin
L = .LFP;
M = NONBOGUS(.L);
M = .M[gt_arg1];
If .MRKREGS Or .M[gt_type] Neq T_VARIABLE Or .M[st_code] Neq S_REGISTER Then
Do
MRK(L)
While (L = .L[gt_fsthread]) Neqa 0;
LFP = .LFP[gt_gthread]
End;
End;
! CALLED FROM: F6, F7, F14, F25
! CALLED WHEN: AFTER PARSING ANY EXPRESSION WHOSE EXECUTION WILL BE
! OPTIONAL, E.G. AFTER EACH BRANCH OF A FORK, OR AFTER
! "DO" EXPRESSION OF A WHILE-DO, DO-WHILE, OR INCR LOOP.
! PURPOSE: SET THE 'PURGEBIT' OF THAT EXPRESSION
! AND ALL ITS SUBEXPRESSIONS.
! ASSUMES: PUSHANDBUMP(CEILING) WAS EXECUTED BEFORE PARSING EXPRESSION,
! BUT MATCHING POPANDDUMP(CEILING) HAS NOT YET BEEN EXECUTED.
!
! notes:
! see page 39 of "The Design of an Optimizing Compiler".
Routine PURGE : Novalue =
Begin
Local
LFP : Ref GT,
LCSP : Ref GT,
C : Integer;
! return if no CSE recognition
If .swit_quick Then
Return;
! loop for each CSE parent
C = .CEILING[stk_data]-.LEVELINC;
FORALLRATORS(I)
Begin
LFP = .GTHASH[.I];
While .LFP Neqa 0 Do
Begin
LCSP = .LFP;
Do
! if created above the ceiling and not already purged
If .LCSP[gt_crlevel] Gtr .C And Not .LCSP[gt_v_purge] Then
Begin
LCSP[gt_v_purge] = TRUE;
LCSP[gt_mklevel] = 0
End
While (LCSP = .LCSP[gt_fsthread]) Neqa 0;
LFP = .LFP[gt_gthread]
End
End
End;
! CALLED FROM: F4
! CALLED WHEN: AFTER EACH BRANCH OF A FORK
! PURPOSE:
! FOR EVERY NODE THAT WAS VALID BEFORE THE BRANCH BUT WAS
! INVALIDATED DURING IT, TURN OFF THE NODE'S 'REAL MARK' BIT,
! BUT TURN ON ITS 'JOIN MARK' BIT TO 'REMEMBER' THE RM BIT.
!
! notes:
! see page 39 of "The Design of an Optimizing Compiler".
!
Routine REFRESH : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
C : Integer;
! purge anything created above the ceiling
PURGE();
If .swit_quick Then
Return;
! loop for all CSE parents
C = .CEILING[stk_data]-.LEVELINC;
FORALLRATORS(I)
Begin
LFP = .GTHASH[.I];
While .LFP Neqa 0 Do
Begin
L = .LFP;
Do
! if marked above the ceiling and either a real or must mark then
! set the join mark and reset MM and RM
If .L[gt_mklevel] Gtr .C And (.L[gt_v_rm] Or .L[gt_v_mm]) Then
Begin
L[gt_v_jm] = TRUE;
L[gt_v_rm] = FALSE;
L[gt_v_mm] = FALSE
End
While (L = .L[gt_fsthread]) Neqa 0;
LFP = .LFP[gt_gthread]
End
End
End;
! CALLED FROM: F5, F6, F7, F14
! CALLED WHEN: AFTER ALL BRANCHES OF A FORK
! PURPOSE: INVALIDATE ANY NODE WHICH WAS INVALIDATED ON SOME BRANCH
! BUT RE-VALIDATED BY 'REFRESH'.
Routine MARKUPDATE : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
C : Integer;
If .swit_quick Then
Return;
! loop for each CSE parent
C = .CEILING[stk_data]-.LEVELINC;
FORALLRATORS(I)
Begin
LFP = .GTHASH[.I];
While .LFP Neqa 0 Do
Begin
L = .LFP;
Do
! if marked above the ceiling then set the must-mark if a join mark
! was set and update the mark level
If .L[gt_mklevel] Gtr .C Then
Begin
Local Q : Ref LVL;
Q = .CEILING[stk_next];
If .L[gt_v_jm] Then
L[gt_v_mm] = TRUE;
L[gt_mklevel] = .Q[stk_data]
End
While (L = .L[gt_fsthread]) Neqa 0;
LFP = .LFP[gt_gthread]
End
End
End;
!
! CALLED FROM: F2, F3, F11, F24
! PURPOSE: PUT AN ENTRY ON THE KILL LIST WITH FIELDS SET TO:
! KCAUSE - .GTINDEX
! KTYPE - .TYPE
! KABC - .ABCOUNT
!
Routine KILL(TYPE,GTINDEX : Ref GT) : Novalue =
Begin
ENLST(.hdr_kill,MAKITEM(.ABCOUNT^48 + .TYPE^32 + .GTINDEX))
End;
Macro
WASUSED(NODEPTR) = WISCHUSED(0, 0,NODEPTR) %,
ISUSED(ZORONE,NODEPTR) = WISCHUSED(ZORONE,1,NODEPTR) %,
WASCHGED(NODEPTR) = WISCHUSED(0, 2,NODEPTR) %,
ISCHGED(ZORONE,NODEPTR) = WISCHUSED(ZORONE,3,NODEPTR) %;
! CALLED WHEN A BOGUS NODE (INITNODE) IS RECOGNIZED AS A C-S-E
! TO SEQUENCE DOWN THE PCSTHREAD FROM INITNODE:
! (1) TOTALLING OCCURRENCE COUNTS
! (2) THREADING VIA CSTHREAD ALL C-S-E'S OFF INITNODE
! (3) SETTING EACH CSPARENT FIELD TO POINT TO BOGUS NODE
Global Routine BINDPCSTHREAD(INITNODE : Ref GT) : Novalue =
Begin
Local
NODE : Ref GT,
L : Ref GT,
VAL : Integer,
COUNT : Integer;
VAL = 0;
COUNT = 0;
NODE = .INITNODE;
! bogus nodes may not be unlinked by DELAY.
!
! Q: why?
INITNODE[gt_v_dont_unlink] = TRUE;
! loop over each node in the 'potential' CSE chain attached to the bogus node.
While (NODE = .NODE[gt_pcsthread]) Neqa 0 Do
Begin
If .NODE[gt_v_csp] Then
Begin
! the first node will become the true CSE parent
If .COUNT Eql 0 Then
COUNT = -1
! all others become children if it.
!
! Q: I don't think this is a true CSE because each branch of a fork must
! evaluate its own instance (it would have been alpha or omega moved
! otherwise). why is the occurence count decremented and won't that
! cause havoc later on?
Else
DECROCC(.NODE);
! set to not unlink. this disables some optimizations in delay but is
! used to make sure that delay does not transform the expression.
NODE[gt_v_dont_unlink] = TRUE;
! accumulate total occurences.
VAL = .VAL + .NODE[gt_occ];
! find the location on the bogus node's CSE chain for this one
L = .INITNODE;
Until .L[gt_csthread] Eqla 0 Do
L = .L[gt_csthread];
L[gt_csthread] = .NODE;
! if not the formal parent then move it after the bogus node on the formal
! parent list.
If Not .NODE[gt_v_fp] Then
Begin
L = .INITNODE[gt_fparent];
Until .L[gt_fsthread] Eqla .NODE Do
L = .L[gt_fsthread];
L[gt_fsthread] = .NODE[gt_fsthread];
NODE[gt_fsthread] = 0;
End;
NODE[gt_v_csp] = FALSE
End
End;
! make the bogus node the CSE parent of all
L = .INITNODE[gt_csthread];
Until .L Eqla 0 Do
Begin
L[gt_csparent] = .INITNODE;
L[gt_pcsthread] = 0;
L = .L[gt_csthread]
End;
! bogus node is no longer the header of a potential CSE chain
INITNODE[gt_pcsthread] = 0;
INITNODE[gt_end_of_pcs] = 0;
INITNODE[gt_occ] = .VAL
End;
! CALLED FROM: GENPSLGBITS
! CALLED TO TURN OFF THE PSLG-BITS OF ALL COMP-EXPS IN SEQUENCE
! BELOW (AND INCLUDING) NODELEX WHEN NODELEX IS DISCOVERED TO BE
! AN "ESSENTIAL CONSTITUENT" OF ITS ANCESTOR. E.G.: A = (F();X = .Y)
! TURNS OFF PSLG BIT OF "X = .Y" AND OF ENCLOSING COMPOUND EXPRESSION.
!
! notes:
! due to the structure of LEXSYM and FLO being in the same
! phase, FLOWAN cannot determine whether all essential
! constituents. e.g., for:
!
! IF .A THEN B ELSE C
!
! it has to assume that B and C are both essential because
! it does not know whether the value of the IF statement is
! used or is discarded.
Routine TURNOFFPSLG(NODE : Ref GT) : Novalue =
Begin
! only works for expression nodes
If .NODE[gt_type] Neq T_NODE Then
Return;
! descend for each compound expression
While .NODE[gt_code] Eql OP_COMPOUND Do
Begin
! turn off postlog bit and descend
NODE[gt_v_postlog] = FALSE;
NODE = .NODE[gt_argv(.NODE[gt_argc]-1)];
If .NODE[gt_type] Neq T_NODE Then
Exitloop
End;
If .NODE[gt_type] Eql T_NODE Then
NODE[gt_v_postlog] = FALSE
End;
! CALLED FROM GENALPHA, GALPHATOPRLG, GCHITOPRLG, AND GOMEGATOPSLG
! FUNCTION:
! PREDICATE INDICATING THAT 'NODELEX' HAS AN ESSENTIAL
! PREDECESSOR (SUCCESSOR) IN THE RANGE [LO,HI].
!
! notes:
! see page 29 of "The Design of an Optimizing Compiler".
!
! this routine tests for pro-dominators/epi-dominators
Routine GALOMBITS(HI : Integer,LO : Integer,NODE : Ref GT) =
Begin
Local
LEX : Ref ST;
! literals and addresses may always be put on the alpha/omega lists
If .NODE[gt_type] Neq T_NODE Then
Return TRUE;
! fake CSE nodes are created by FAKECSE in SYNTAX and act as placeholders
! for bind expressions.
If .NODE[gt_code] Eql OP_FAKE_CSE Then
NODE = .NODE[gt_csparent];
! make sure its not bogus because we are accessing the operands
NODE = NONBOGUS(.NODE);
! if any operand may not be placed on the alpha/omega list then
! this node may not
FORALLRANDS(I,.NODE)
Begin
If Not GALOMBITS(.HI,.LO,.NODE[gt_argv(.I)]) Then
Return FALSE
End;
! a '.' operator may not be moved if:
!
! - the dotting of an expression
!
! - there is a kill entry which kills this access in the range
!
! - there is a store entry for the variable in the range
If .NODE[gt_code] Eql OP_DOT Then
Begin
LEX = FINDNAME(.NODE[gt_arg1]);
If .LEX Eqla 0 Then
Return FALSE; ! SEE NOTE, BELOW
If SEARCHFORKILLS(.LEX,.HI,.LO,1) Then
Return FALSE;
If ABCBETW(.HI,.LO,.LEX[st_var_chg_list]) Then
Return FALSE
End
! a '=' operator may not be moved if:
!
! - the storing into an expression
!
! - there is a kill entry which kills a store in this range
!
! - there is a fetch entry for the variable in the range
Else If .NODE[gt_code] Eql OP_STORE Then
Begin
LEX = FINDNAME(.NODE[gt_arg1]);
If .LEX Eqla 0 Then
Return FALSE; ! SEE NOTE, BELOW
If SEARCHFORKILLS(.LEX,.HI,.LO,0) Then
Return FALSE;
If ABCBETW(.HI,.LO,.LEX[st_var_use_list]) Then
Return FALSE
End;
! IN THE TWO CASES ABOVE, IT WOULD BE UNWISE TO SUBSTITUTE
! " ... THEN RETURN (NOT .MRKFLG);" FOR " ... THEN RETURN 0;".
! THE Q SWITCH TELLS THE COMPILER WHETHER "A = " HAS ANY EFFECT
! ON ".(.B+.C)"; BUT REGARDLESS OF WHETHER THE Q SWITCH IS ON,
! "(.B+.C) = " HAS AN EFFECT ON ".(.B+.C)" . SO WE DON'T WANT
! TO INDICATE, BY RETURNING 1 AT EITHER OF THE ABOVE POINTS,
! THAT THE CODE FOR .(.B+.C) CAN BE MOVED FORWARD OVER THE CODE
! FOR (.B+.C) = .
! node is ok to be placed on an alpha/omega list
Return TRUE
End;
! EXAMINES AN OMEGA LIST FOR ENTRIES WHICH CAN BE ENTERED IN THE
! POSTLOG SET OF THE ENCLOSING LINEAR BLOCK
!
! notes:
! see page 42 of "The Design of an Optimizing Compiler".
Routine GOMEGATOPSLG(HDR : Ref LSTHDR,LO : Integer) : Novalue =
Begin
Local
L : Ref ITEM;
! loop for each omega list entry
L = .HDR;
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
! if the entry is not killed between here and abcount then move it
! to the postlog list
If GALOMBITS(.ABCOUNT,.LO,.L[itm_int_ldata(1)]) Then ! 2
ENLST(.hdr_postlog[stk_data],MAKITEM(.L[itm_int_data(1)]))
End
! % 1 % HAD TO BE REPLACED BY % 2 %, UNFORTUNATELY. THE PROBLEM IS
! THAT, IN A LIST ENTRY, THE 'ITEMFPARENT' AND 'ABCVAL' FIELDS ARE IN
! THE SAME PLACE. GENOMEGA FILLS THE LATTER, ZONKING THE FORMER, AND
! SINCE THIS ROUTINE DOESN'T GET CALLED TILL AFTER GENOMEGA, THE
! 'ITEMFPARENT' FIELD HAS TO BE CONSIDERED INVALID. THE SOLUTION TO
! THE PROBLEM IS THAT AN OMEGA LIST ENTRY IS MADE UP OF SEVERAL POSTLOG
! LIST ENTRIES, EACH OF WHICH HAS ITS OWN 'ITEMFPARENT' FIELD, AND
! ALL THESE FORMAL PARENTS ARE THE SAME AS THE FORMAL PARENT OF THE
! WHOLE OMEGA LIST ENTRY.
End;
! EXAMINES AN ALPHA LIST FOR ENTRIES WHICH CAN BE ENTERED IN THE
! PROLOG SET OF THE ENCLOSING LINEAR BLOCK
Routine GALPHATOPRLG(HDR : Ref LSTHDR,HI : Integer) : Novalue =
Begin
Local
L : Ref ITEM,
FPAR : Ref GT;
! loop for each alpha list entry
L = .hdr;
While (L = .L[itm_rlink]) Neqa .hdr Do
Begin
! if the entry is not killed in the range of the start of this block
! to here then add it to the prolog list. Note: the alpha list
! entry rather than the node is question is added.
FPAR = .L[itm_fparent];
If GALOMBITS(.HI,.ABCBASE[stk_data],.FPAR) Then
ENLST(.hdr_prolog[stk_data],MAKITEM(.FPAR^32 + 1 + .L)) ! note the 1
End
End;
! CALLED TO GENERATE PSLG-BITS FOR AN EXPRESSION IN A LINEAR BLOCK
!
! notes:
! see page 42 of "The Design of an Optimizing Compiler".
Routine GENPSLGBITS(NODE : Ref GT) =
Begin
Local
VAL : Integer,
L : Ref GT,
RANDVAL : Integer;
! handle omega lists differently
If .NODE[gt_type] Eql T_QUEUE And .NODE[que_code] Eql T_OMEGA Then
Begin
GOMEGATOPSLG(.NODE[que_head],.NODE[que_abc]);
Return FALSE
End;
! literals and addresses are always in the postlog
If .NODE[gt_type] Neq T_NODE Then
Return TRUE;
! control statements are never moved
If .NODE[gt_v_flow] Then
Return FALSE;
! examine all the operands of this node and see if they all may go in
! the postlog.
VAL = TRUE;
FORALLRANDS(I,.NODE)
Begin
L = .NODE[gt_argv(.I)];
RANDVAL = GENPSLGBITS(.L);
! if a compound statement, set the postlog bit as it may be non essential
If .NODE[gt_code] Eql OP_COMPOUND Then
Begin
If .RANDVAL And .L[gt_type] Eql T_NODE Then
L[gt_v_postlog] = TRUE
End
Else
! this operand is essential. turn off any postlog bits which may have been set
Begin
TURNOFFPSLG(.L);
If Not .RANDVAL Then
Return FALSE
End;
VAL = .VAL And .RANDVAL
End;
Selectone .NODE[gt_code] Of Set
! call parameters may never move without their call and calls may not
! move period because any call side effects are not known.
[OP_CALL]:
VAL = FALSE;
! a '.' may not move if there is a store between here and there
[OP_DOT]:
If ISCHGED(1,.NODE) Then
VAL = FALSE;
! a '=' may not moved if there is a fetch between here and there
[OP_STORE]:
If ISUSED(1,.NODE) Then
VAL = FALSE
Tes;
! if something could not move
If Not .VAL Then
Return FALSE;
! bind expressions may not move
L = .NODE[gt_csparent];
Until (L = .L[gt_csthread]) Eqla 0 Do
If .L[gt_code] Eql OP_FAKE_CSE Then
Begin
NODE[gt_v_postlog] = FALSE;
Return FALSE
End;
! set the postlog bit only if there is a cse parent
If .NODE[gt_code] Neq OP_COMPOUND Then
NODE[gt_v_postlog] = .NODE[gt_v_csp];
Return TRUE
End;
! GENERATES THE POSTLOG SET FOR A LINEAR BLOCK (B) AND
! ALSO BUILDS THE SET: B-(PROLOG POSTLOG) WHICH IS
! CALLED THE MU LIST OF THE LINEAR BLOCK
!
! note: nothing uses the MU list
Routine GENMUPSLGLST(NODE : Ref GT) : Novalue =
Begin
If Not .swit_optimize Then
Return;
! only expressions may be placed in the postlog
If .NODE[gt_type] Neq T_NODE Then
Return;
! control statements may not move
If .NODE[gt_v_flow] Then
Return;
! if part of the postlog, move it onto the list
If .NODE[gt_v_postlog] Then
ENLST(.hdr_postlog[stk_data],MAKITEM(.NODE[gt_fparent],.NODE))
Else
!
! IF .NODE[CSP] THEN IF NOT .NODE[PRLGBIT] THEN
! ENLST(.hdr_mu[stk_data],MAKITEM(.NODE[gt_fparent],.NODE));
!
! this is not part of the postlog but maybe one of its children is
FORALLRANDS(I,.NODE)
GENMUPSLGLST(.NODE[gt_argv(.I)])
End;
! GENERATES THE EPILOG SET FOR A LINEAR BLOCK BY DISCOVERING ALL
! AVAILABLE (I.E. UNMARKED) C-S-E'S.
Routine GENEPLGLST(NODE : Ref GT) : Novalue =
Begin
Local
L : Ref GT,
LCS : Ref GT,
F : Integer;
If Not .swit_optimize Then
Return;
! only expressions may go on the epilog
If .NODE[gt_type] Neq T_NODE Then
Return;
! if quick then there are no CSE's
If .swit_quick Then
Return;
! loop for all CSE's
F = .FLOOR[stk_data];
FORALLRATORS(I)
Begin
L = .GTHASH[.I];
Until .L Eqla 0 Do
Begin
LCS = .L;
Do
! if not purged and not marked and created in this level then
! move it to the epilog and stop searching this formal list
If Not .LCS[gt_v_purge] And Not .LCS[gt_v_rm] And
.LCS[gt_crlevel] Geq .F Then
Begin
ENLST(.hdr_epilog[stk_data],MAKITEM(.L^32 + .LCS));
Exitloop
End
While (LCS = .LCS[gt_fsthread]) Neqa 0;
L = .L[gt_gthread]
End
End
End;
! GENERATES CHANGE AND USE LIST HEADERS FOR THE DECLARED
! VARIABLES WHOSE SYMBOL TABLES ENTRY IS S. ALSO ENTERS
! USE AND CHANGE LIST ENTRIES TO PREVENT THE MOVE OF A
! VARIABLE REFERENCE BACKWARD PAST DECLARATION POINT.
Global Routine INITSYMLSTS(S : Ref ST) : Novalue =
Begin
If .swit_quick Then
Return;
! no lists for 'outer block' st entries
If .S[st_scope] Eql 0 Then
Return;
S[st_var_use_list] = MAKHDR(VUSEREMOVE,VUSEENTER);
S[st_var_chg_list] = MAKHDR(VCHGREMOVE,VCHGENTER);
ENTVCHGLST(.S,0);
ENTVUSELST(.S,0)
End;
! ENTER VARIABLE USE LIST
! CALLED FROM: GENGT, INITSYMLSTS, F10, F11
! ENTERS AN ITEM ON THE USE LIST OF THE NAME (IF ANY) INVOLVED
! IN THE EXPRESSION POINTED TO BY "OPRND" REFLECTING THE FACT THAT
! A REFERENCE TO THE VALUE OCCURED IN THE EXPRESSION
! "GTINDEX". THE FORM OF THE ENTRY IS: ABCOUNT,,GTINDEX.
Global Routine ENTVUSELST(OPRND : Ref GT,GTINDEX : Ref GT) : Novalue =
Begin
Local
S : Ref ST;
! find the name referenced. if no name is involved then generate
! a general kill if the mark flag is set
S = FINDNAME(.OPRND);
If .S Eqla 0 Then
Begin
If .swit_mark Then
KILL(3,.GTINDEX)
End
! if a storage variable and it has a use list then add an entry
! to the use list
!
! Q: how can it not be a storage variable?
!
! A: maybe if it's a routine
Else If ISSTVAR(S) And .S[st_var_use_list] Neqa 0 Then
ENLST(.S[st_var_use_list],MAKITEM(.ABCOUNT^32 + .GTINDEX))
End;
! ENTER VARIABLE CHANGE LIST
! CALLED FROM: GENGT, INITSYMLSTS, F10, F11
! SAME AS ENTVUSELST EXCEPT THAT THE NAME IN OPRND WAS THE
! TARGET OF A STORE.
Global Routine ENTVCHGLST(OPRND : Ref GT,GTINDEX : Ref GT) : Novalue =
Begin
Local
S : Ref ST;
! find the name referenced. is no name is invoved then note a
! general kill unless the mark flag is set
S = FINDNAME(.OPRND);
If .S Eqla 0 Then
Begin
If .swit_mark Then
KILL(2,.GTINDEX)
End
! if a storage variable and it has a change list then add an entry
! to the change list
Else If ISSTVAR(S) And .S[st_var_chg_list] Neqa 0 Then
ENLST(.S[st_var_chg_list],MAKITEM(.ABCOUNT^32 + .GTINDEX))
End;
! GENERATE PRLG LIST AND BITS. ALWAYS CALLED WITH PTR TO GT-NODE
!
! called by: GENGT
Global Routine GENPRLG(NODE : Ref GT) : Novalue =
Begin
Macro
ISRELOP(X)=ONEOF(X,OP_GTR,OP_LEQ,OP_LSS,OP_GEQ,OP_EQL,OP_NEQ,
OP_GTRU,OP_LEQU,OP_GEQU,OP_LSSU,OP_EQLU,OP_NEQU) %;
Local
PARENT : Ref GT;
If .swit_quick Then
Return;
If Not .swit_optimize Then
Return;
! only operators and '=' may go on the prolog
If .NODE[gt_code] Gtr OP_STORE Then
Return;
! THIS IS A DECISION THAT OUGHT, REALLY, TO BE MADE IN DELAY.
! THE IDEA IS THAT RELATIONAL OPERATOR NODES AREN'T ALPHA- OR
! CHI-LISTED, BECAUSE THEY'RE USUALLY IN CONTEXTS WHERE IT'S
! CHEAPER TO PUT OUT A 'CMP' (OR 'TST') INSTRUCTION ON EACH
! BRANCH OF A FORK (OR IN A LOOP) THAN TO GENERATE A REAL
! RESULT (1 OR 0) BEFORE THE FORK (OUTSIDE THE LOOP).
If ISRELOP(.NODE[gt_code]) Then
Return;
! examine all operands. if the operand is a flow control operator
! or is not on the prolog then return
FORALLRANDS(I,.NODE)
Begin
Local P : Ref GT;
P = .NODE[gt_argv(.I)];
If .P[gt_type] Eql T_NODE Then
Begin
If .P[gt_v_flow] Then
Return;
If Not .P[gt_v_prolog] Then
Return
End
End;
! special cases
Select .NODE[gt_code] Of Set
! call parameters never move
[OP_CALL]:
Return;
! a '.' cannot move if there was a store between the start of the block and here
[OP_DOT]:
If WASCHGED(.NODE) Then
Return;
! a '=' cannot move if there was a fetch between the start of the block and here
[OP_STORE]:
If WASUSED(.NODE) Then
Return
Tes;
! if not a CSE parent then mark it as being in the prolog if the parent
! is in the prolog or the parent was created outside the block. do not
! place it in the prolog list
If Not .NODE[gt_v_csp] Then
Begin
PARENT = .NODE[gt_csparent];
NODE[gt_v_prolog] = .PARENT[gt_v_prolog] Or
(.PARENT[gt_abc] Leq .ABCBASE[stk_data])
End
Else
! add CSE heads to the prolog list
Begin
ENLST(.hdr_prolog[stk_data],MAKITEM(.NODE[gt_fparent]^32 + .NODE));
NODE[gt_v_prolog] = TRUE
End
End;
! WAS-IS CHANGED-USED ...
! ARGUMENTS:
! NODEPTR: A = NODE OR A . NODE
! ZORONE: ZERO OR ONE; VALID ONLY FOR "IS" CHANGED-USED.
! CALLED TO CHECK THE VCHGLST OR VUSELST WHEN A ELEMENT IS
! CONSIDERED FOR INSERTION ON A FLOLST
! S=0 --> WASUSED
! S=1 --> ISUSED
! S=2 --> WASCHGED
! S=3 --> ISCHGED
Routine WISCHUSED(ZORONE,S,NODEPTR : Ref GT)=
Begin
Local
LEX : Ref ST,
HI : Integer,
LO : Integer,
P : Ref LSTHDR;
! find the name referenced. if there is no name then assume the value
! was/is changed
LEX = FINDNAME(.NODEPTR[gt_arg1]);
If .LEX Eqla 0 Then
Return TRUE;
! if not a storage variable then assume changed
If Not ISSTVAR(LEX) Then
Return TRUE;
! compute the bounds to search
If .S<0,1> Then ! to end for epilog/postlog
Begin
HI = .ABCOUNT;
LO = .NODEPTR[gt_abc]+.ZORONE
End
Else
Begin ! to start for prolog
HI = .NODEPTR[gt_abc]-1;
LO = .ABCBASE[stk_data]
End;
! if a kill exists in the range then was/is changed
If SEARCHFORKILLS(.LEX,.HI,.LO,.S<1,1>) Then
Return TRUE;
! determine which list to check. if the list does not exist then
! assume the variable was/is changed
P = (If .S<1,1> Then .LEX[st_var_chg_list] Else .LEX[st_var_use_list]);
If .P EQLA 0 Then
Return TRUE;
! check the list
Return ABCBETW(.HI,.LO,.P)
End;
! ABCOUNT BETWEEN
! CALLED FROM GALOMBITS, WISCHUSED
! ATOMIC BLOCK COUNT BETWEEN ...
! PREDICATE INDICATING THERE IS AN ENTRY ON LIST HEADED BY HDR
! WHOSE ABCVAL IS IN THE CLOSED INTERVAL [LO,HI]
Routine ABCBETW(HI : Integer,LO : Integer,HDR : Ref LSTHDR) =
Begin
Local
I : Ref ITEM;
! loop for each list entry
I = .HDR;
Until (I = .I[itm_rlink]) Eqla .HDR Do
Begin
If .I[itm_abc] Lss .LO Then
Exitloop;
If .I[itm_abc] Leq .HI Then
Return TRUE
End;
Return FALSE
End;
!
! SUPPLEMENTS THE ACTION OF 'ABCOUNT BETWEEN' BY LOOKING
! ON THE KILL LIST.
!
! ARGUMENTS:
! STVAR - THE VARIABLE WHOSE CHANGED OR USED STATUS IS IN QUESTION
! USEORCHG - BOOLEAN; TRUE IF CHANGE (RATHER THAN USE) IS BEING
! LOOKED FOR
! HI, LO - SEE 'ABCOUNT BETWEEN'
!
! KILL TYPES:
! 0 - A RETURN. A USE LIST ENTRY FOR ALL VARIABLES.
! 1 - A LEAVE. SAME AS A RETURN, BUT KILL LIST ENTRY
! DISAPPEARS WHEN SYNTAX PROCESSING FOR THE LABEL ENDS.
! 2 - STORE INTO CALCULATED ADDRESS (.A = EXPR). A CHANGE
! LIST ENTRY FOR ALL BUT REGISTER VARIABLES.
! 3 - FETCH FROM A CALCULATED ADDRESS (VAR = ..A). A USE
! LIST ENTRY FOR ALL BUT REGISTER VARIABLES.
! 4 - A ROUTINE CALL. A CHANGE AND USE, FOR GLOBAL,
! EXTERNAL, AND OWN VARIABLES.
! 5 - AN INLINE. A CHANGE AND USE, FOR ALL VARIABLES.
!
! Q: why is a return a use of all variables?
Routine SEARCHFORKILLS(STVAR : Ref ST,HI : Integer,LO : Integer,USEORCHG) =
Begin
Bind
matrix = Uplit Byte (
3,3,2,3,0,0,1,
3,3,0,0,0,0,1,
3,3,2,3,0,0,1,
3,3,2,3,1,0,1,
3,3,2,3,1,0,1,
3,3,2,3,1,0,1
) : Vector[36,Byte];
Local
TYPE : Integer,
I : Ref ITEM,
j : Integer;
! compute index into matrix
J = (.STVAR[st_code] - LOWVARTYPE) * 6;
! loop for each kill list entry
I = .hdr_kill;
Until (I = .I[itm_rlink]) Eqla .hdr_kill Do
Begin
If .I[itm_kill_abc] Lss .LO Then
Exitloop;
If .I[itm_kill_abc] Leq .HI Then
Case .matrix[.J + .I[itm_kill_type]] From 0 To 3 Of Set
! does not kill
[0]:
0;
! always kills
[1]:
Return TRUE;
[2]:
! kills a change
If .USEORCHG Then
Return TRUE;
! kills a use
[3]:
If Not .USEORCHG Then
Return TRUE
Tes
End;
Return FALSE
End;
! CALLED FROM: GFDOWHILE, F19
! GENERATES EPILOG SET FOR WHILE EXPRESSION IN WHILE-DO CONSTRUCT
Routine GFWHILE : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
! only expressions may have a flow list
If .SYM[gt_type] Eql T_NODE Then
Begin
LSTPTR = SYM[gt_flow] = GETSPACE(SZ_FLOLIST);
LSTPTR[flow_epilog] = hdr_epilog[stk_data] = MAKHDR(EPLGREMOVE,EPLGENTER);
GENEPLGLST(.SYM);
SYM[gt_v_flow] = TRUE
End
End;
! CALLED FROM: F26
! GENERATES EPILOG SET FOR COMBINED DO & WHILE EXPRESSIONS OF
! A DO-WHILE CONSTRUCT; IF "WHILE" EXPR. IS A NON-GRAPH-TABLE
! LEXEME, ATTACHES EPILOG LIST TO "DO" EXPRESSION.
Routine GFDOWHILE : Novalue =
Begin
If .SYM[gt_type] Eql T_NODE Then
GFWHILE()
Else
Begin
SYM = .STK[.TOS-1]; ! GET "DO" EXPRESSION
GFWHILE();
SYM = .STK[.TOS] ! RETRIEVE "WHILE" EXPRESSION
End
End;
! CALLED BY F4
! CALLED AFTER EACH BRANCH OF A FORK
! GENERATES PROLOG, EPILOG, AND POSTLOG SETS FOR LINEAR BLOCK
! WHICH FORMS BRANCH IN FORKED CONSTRUCT
Routine GFBRANCH : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
! only expressions may have a flow list
If .SYM[gt_type] Neq T_NODE Then
Return;
LSTPTR = SYM[gt_flow] = GETSPACE(SZ_FLOLIST);
LSTPTR[flow_prolog] = .hdr_prolog[stk_data];
!
! LSTPTR[MULSTF] = hdr_mu[stk_data] = MAKHDR(MUREMOVE,MUENTER);
!
LSTPTR[flow_postlog] = hdr_postlog[stk_data] = MAKHDR(PSLGREMOVE,PSLGENTER);
LSTPTR[flow_epilog] = hdr_epilog[stk_data] = MAKHDR(EPLGREMOVE,EPLGENTER);
If Not .swit_quick Then
Begin
GENPSLGBITS(.SYM);
GENMUPSLGLST(.SYM);
GENEPLGLST(.SYM)
End;
SYM[gt_v_flow] = TRUE
End;
! CALLED FROM F16, F17, F18
! GENERATES PROLOG FOR LINEAR BLOCK WHICH FORMS BODY (AND
! PERHAPS PREDICATE) OF LOOPING CONSTRUCT
Routine GFLOOP : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
! only expresisons may have flow lists
If .SYM[gt_type] Eql T_NODE Then
Begin
LSTPTR = SYM[gt_flow] = GETSPACE(SZ_FLOLIST);
LSTPTR[flow_prolog] = .hdr_prolog[stk_data];
SYM[gt_v_flow] = TRUE
End
End;
! GENERATE ALPHA (ALOMFLAG=1) AND OMEGA SETS FOR FORKED CONTROL
! ENVIRONMENTS.
! AN ALPHA (OMEGA) ELEMENT FOR AN N-BRANCH FORK:
! 0: LLINK,,RLLINK
! 1: FORMAL-PARENT,,NUM-OF-BRANCHES
! AND N ENTRIES WHERE THE K-TH IS
! FORMAL-PARENT,,X
! AND WHERE IF LOW ORDER BIT OF X IS ON THEN THE X POINTS TO
! ANOTHER ALPHA ELEMENT ELSE X IS A NODE ON THE K-TH BRANCH.
Routine GENALOMLST(ALOMFLAG : Boolean) =
Begin
Local
L : Ref ITEM,
NODE : Ref GT,
HDR : Ref LSTHDR,
LSTPTR : Ref FLOLSTPTR;
! choose the list to scan
LSTPTR = .Block[.STK[.LASTMARK+3],gt_flow];
HDR = (If .ALOMFLAG
Then .hdr_alpha[que_head]
Else .hdr_omega[que_head]);
! generate the initial list
MAKINTLST(.TOS-(.LASTMARK+3),
If .ALOMFLAG
Then .LSTPTR[flow_prolog]
Else .LSTPTR[flow_postlog],
.HDR);
! now discarded
If .ALOMFLAG Then
LSTPTR[flow_prolog] = 0
Else
LSTPTR[flow_postlog] = 0;
! loop for the other lists
Incr I From .LASTMARK+4 To .TOS-1 Do
Begin
LSTPTR = .Block[.STK[.I],gt_flow];
SORTFINT(.I-(.LASTMARK+2),
.HDR,
If .ALOMFLAG
Then .LSTPTR[flow_prolog]
Else .LSTPTR[flow_postlog]);
If .ALOMFLAG Then
LSTPTR[flow_prolog] = 0
Else
LSTPTR[flow_postlog] = 0
End
End;
! GENERATE THE ALPHA LIST FOR A FORKED CONTROL CONSTRUCT
Routine GENALPHA : Novalue =
Begin
Local
L : Ref ITEM,
M : Ref GT,
NODE : Ref GT,
ALPHNODE : Ref GT,
L2 : Ref ITEM,
VAL : Integer,
HDR : Ref ITEM,
N : Ref ITEM;
! generate the intersections of all the prologs
GENALOMLST(TRUE);
If EMPTY(.hdr_alpha[que_head]) Then
Return;
!
! AT THIS POINT 'M' HOLDS A POINTER TO THE BOOLEAN OF AN
! IF-THEN-ELSE EXPRESSION, OR THE CASE INDEX OF A CASE EXPRESSION.
! THE FOLLOWING CODE CHECKS WHETHER EACH ALPHA-LIST ENTRY HAS
! AN ESSENTIAL PREDECESSOR IN M.
!
! when the prolog sets were built for the THEN and ELSE parts,
! they only considered essential predecessors within themselves.
! now we consider essential predecessors in the IF condition.
M = .STK[.LASTMARK+2];
VAL = .M[gt_abc];
HDR = .hdr_alpha[que_head];
! loop, discarding all alpha list entries which have essential predecessors
! in 'M'
L = .HDR[itm_rlink];
While .L Neqa .HDR Do
Begin
L2 = .L;
L = .L[itm_rlink];
If Not GALOMBITS(.VAL,.hdr_alpha[que_abc],.L2[itm_fparent]) Then
RELITEM(.L2,SZ_INT_ITEM(.L2[itm_size]))
End;
! return if nothing left
If EMPTY(.hdr_alpha[que_head]) Then
Return;
! place any alpha list entries onto the prolog which fit
GALPHATOPRLG(.hdr_alpha[que_head],.hdr_alpha[que_abc]);
!
! AT THIS POINT, THE CURRENT ALPHA LIST CONTAINS A BUNCH OF ENTRIES,
! SOME OF WHICH ARE POINTED TO BY PROLOG LIST ENTRIES, AND SOME OF
! WHICH CONTAIN POINTERS TO OTHER LIST ENTRIES RATHER THAN TO NODES.
! FOR GENALPHA'S OWN USE AND FOR DELAY, TNBIND, AND CODE, THE ALPHA
! LIST ENTRIES SHOULD ONLY CONTAIN POINTERS TO GT-NODES. THEREFORE
! THE FOLLOWING CODE MAKES A NEW COPY OF EACH ENTRY; THE OLD COPY IS
! STILL POINTED TO BY THE PROLOG LIST ENTRY (IF ANY), AND THE NEW
! COPY, WHICH REPLACES IT ON THE ALPHA LIST, HAS POINTERS ONLY TO NODES.
!
! Q: the old entry is removed from the alpha list but is not released
! because it may have been placed on the prolog. if it was not
! placed on the prolog then this looks like a memory leak. am
! I missing something?
L = .HDR[itm_rlink];
While .L Neqa .HDR Do
Begin
Local M : Ref ITEM;
! create a new item
M = GETSPACE(SZ_INT_ITEM(.L[itm_size]));
M[itm_llink] = .M;
M[itm_rlink] = .M;
M[itm_size] = .L[itm_size];
M[itm_fparent] = .L[itm_fparent];
! replace L with M
LINK(.M,.L[itm_llink]);
DELINK(.L);
! loop, copying item entries from L to M
Incr I From 1 To .L[itm_size] Do
Begin
N = .L[itm_int_rdata(.I)];
! the purge bit is reset for entries on the alpha list. entries coming
! from another alpha list supposedly already have the prolog bit turned
! off but it wouldn't hurt to turn it off here also.
If Not .N<0,1> Then
Block[.N,gt_v_purge] = FALSE
Else
Do
Begin
N<0,1> = 0;
N = .N[itm_int_rdata(1)]
End
While .N<0,1>;
M[itm_int_rdata(.I)] = .N
End;
L = .M[itm_rlink]
End;
! END OF ABOVE NOTED CODE
! another pass over alpha entries...
!
! this pass takes the first node in each alpha entry and makes it
! the CSE parent of the rest of the nodes in the entry.
L = .HDR;
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
! select the first intersection item as the CSE head and link
! all the other item entries to it
ALPHNODE = .L[itm_int_rdata(1)];
VAL = 0;
Incr I From 2 To .L[itm_size] Do
Begin
NODE = .L[itm_int_rdata(.I)];
! count occurances
VAL = .VAL+.NODE[gt_occ];
! add to end of CSE thread
M = .ALPHNODE;
Until .M[gt_csthread] Eqla 0 Do
M = .M[gt_csthread];
M[gt_csthread] = .NODE;
! remove from formal parent list. Note: the first entry is guaranteed
! to come before this entry on the formal parent chain so the gthash
! chains do not have to be adjusted
M = .ALPHNODE[gt_fparent];
Until .M[gt_fsthread] Eqla .NODE Do
M = .M[gt_fsthread];
M[gt_fsthread] = .NODE[gt_fsthread];
NODE[gt_fsthread] = 0;
! no longer a CSE parent
NODE[gt_v_csp] = FALSE;
NODE[gt_v_mustgencode] = FALSE;
End;
! now assign the CSE parent to all the entries
M = .ALPHNODE;
Until (M = .M[gt_csthread]) Eqla 0 Do
M[gt_csparent] = .ALPHNODE;
! update the occurence count
ALPHNODE[gt_occ] = .ALPHNODE[gt_occ] + .VAL;
! adjust the occurence count for all expression operands
! and mark all operands. a later pass will remove these
! from the alpha list since their parent is already on the list
FORALLRANDS(I,.ALPHNODE)
Begin
Local P : Ref GT;
P = .ALPHNODE[gt_argv(.I)];
If .P[gt_type] Eql T_NODE Then
Begin
P[gt_occ] = .P[gt_occ] - (.L[itm_size]-1);
P[gt_v_alpha] = TRUE
End
End
End;
! another pass through the alpha list
!
! this pass removes nodes whoose parent is on the alpha list.
! it also reverses the order of the alpha list.
L = .hdr_alpha[que_head];
L = .L[itm_rlink];
While .L Neqa .hdr_alpha[que_head] Do
Begin
NODE = .L[itm_int_rdata(1)];
L2 = .L;
L = .L[itm_rlink];
! if marked then this is a child of another node already on the alpha
! list so remove it
If .NODE[gt_v_alpha] Then
RELITEM(.L2,SZ_INT_ITEM(.L2[itm_size]))
Else
Begin
L2[itm_abc] = .NODE[gt_abc];
! mark all other intersection entries as DONTUNLINK
Decr I From .L2[itm_size] To 1 Do
Begin
M = .L2[itm_int_rdata(.I)];
M[gt_v_dont_unlink] = TRUE
End;
! reverse the list order
ENLST(.hdr_alpha[que_head],.L2)
End
End;
! now set the alpha bit on all entries on the alpha list.
! this marks the node as ineligible for omega motion.
While (L = .L[itm_rlink]) Neqa .hdr_alpha[que_head] Do
Begin
NODE = .L[itm_int_rdata(1)];
NODE[gt_v_alpha] = TRUE
End
End;
! decrement occurence count for non-CSE parent omega entries
Routine OMEGDECR(NODE : Ref GT) : Novalue =
Begin
Local
L : Ref GT,
P : Ref GT;
! non-bogus because we need to access the arguments
L = NONBOGUS(.NODE);
! apply recursively to all children
FORALLRANDS(I,.L)
Begin
P = .L[gt_argv(.I)];
If .P[gt_type] Eql T_NODE Then
OMEGDECR(.P)
End;
! adjust the occurence count of our CSE parent
p = .NODE[gt_csparent];
p[gt_occ] = .p[gt_occ] - 1;
! if it turns out this is a CSE parent with other CSE entries then
! turn on the MUSTGENCODE for them.
!
! Q: why turn off MUSTGENCODE?
!
! A: with omega motion, the nodes are not actually moved but are only
! placed on the omega list. thus the original nodes are seen before
! the omega list is actually processed. turning off the mustgencode
! bit causes these nodes to be passed over until the omega list is
! processed. this is also why we see the mustgencode bit being set
! in pulse routines.
If .p[gt_occ] Gtr 0 And .NODE[gt_v_csp] And Not .NODE[gt_v_alpha] Then
Begin
L = .NODE;
While (L = .L[gt_csthread]) Neq 0 Do
Begin
NODE[gt_occ] = .NODE[gt_occ]-1;
L[gt_v_mustgencode] = TRUE
End
End
End;
! decrement occurent count for CSE parent omega entries
Routine OMEGHEADECR(NODE : Ref GT,DEPTH : Integer) : Novalue =
Begin
Local
L : Ref GT,
P : Ref GT;
! need non-bogus due to access of arguments
L = NONBOGUS(.NODE);
! apply recursively to all arguments
FORALLRANDS(I,.L)
Begin
P = .L[gt_argv(.I)];
If .P[gt_type] Eql T_NODE Then
OMEGHEADECR(.P,.DEPTH+1)
End;
p = .NODE[gt_csparent];
! if not the top level and this is a CSE head with CSE followers
! then turn on MUSTGENCODE for the followers
If .DEPTH Gtr 0 And .p[gt_occ] Gtr 1 And .NODE[gt_v_csp] Then
Begin
L = .NODE;
While (L = .L[gt_csthread]) Neqa 0 Do
Begin
NODE[gt_occ] = .NODE[gt_occ]-1;
L[gt_v_mustgencode] = TRUE
End
End
End;
! test whether a node or any of its children is on the alpha list
!
! notes:
! nodes are not moved to the omega list if they are already on
! the alpha list.
Routine CHECKALPHA(NODE : Ref GT) =
Begin
Local
OPND : Ref GT;
! see if this node is on the alpha list
If .NODE[gt_v_alpha] Then
Return TRUE;
! see if any of its children is on the alpha list
FORALLRANDS(I,.NODE)
Begin
OPND = .NODE[gt_argv(.I)];
If .OPND[gt_type] Eql T_NODE Then
If CHECKALPHA(.OPND) Then
Return TRUE
End;
Return FALSE
End;
! GENERATE THE OMEGA LIST FOR A FORKED CONTROL CONSTRUCT
Routine GENOMEGA : Novalue =
Begin
Local
L : Ref ITEM,
L2 : Ref ITEM,
OMEGNODE : Ref GT,
SRCNODE : Ref GT,
NODE : Ref GT,
SIZE : Integer;
Label
aaa;
! generate the omega intersection list
GENALOMLST(FALSE);
If Not EMPTY(.hdr_omega[que_head]) Then
Begin
! loop for each omega list entry
L = .hdr_omega[que_head];
While (L = .L[itm_rlink]) Neqa .hdr_omega[que_head] Do
aaa: Begin
OMEGNODE = .L[itm_int_rdata(1)];
! it can't be on both the alpha and omega lists
If CHECKALPHA(.OMEGNODE) Then
Begin
SIZE = .L[itm_size];
L = .L[itm_llink];
RELITEM(.L[itm_rlink],SZ_INT_ITEM(.SIZE));
Leave aaa
End;
! check each intersection item entry. if it is on an inner omega
! list then make the entries CSE followers again and release the inner
! list.
!
! Q: what about the occurence count?
!
! Q: what about the INNEROMEGAENT field? should it not be set to 0?
Incr I From 1 To .L[itm_size] Do
Begin
SRCNODE = .L[itm_int_rdata(.I)];
L2 = .SRCNODE[gt_inner_omega];
If .L2 Neqa 0 Then
Begin
SIZE = .L2[itm_size];
Incr K From 2 To .SIZE Do
Begin
NODE = .L2[itm_int_rdata(.K)];
NODE[gt_v_mustgencode] = FALSE
End;
RELITEM(.L2,SZ_INT_ITEM(.SIZE))
End
End;
! decrement the occurence count for the item entries because
! omega entries are not really CSE's (?) but are merged into a
! single node
Incr I From 2 To .L[itm_size] Do
Begin
NODE = .L[itm_int_rdata(.I)];
OMEGDECR(.NODE);
NODE[gt_v_omega] = TRUE;
End;
! now adjust the omega head entry
OMEGHEADECR(.OMEGNODE,0);
OMEGNODE[gt_v_omega] = TRUE;
End;
! loop for each omega entry and mark the inner omega entry it is on
L = .hdr_omega[que_head];
L = .L[itm_rlink];
While .L Neqa .hdr_omega[que_head] Do
Begin
OMEGNODE = .L[itm_int_rdata(1)];
OMEGNODE[gt_inner_omega] = .L;
L[itm_abc] = .OMEGNODE[gt_abc];
! reverse the omega list also
L = .L[itm_rlink];
ENLST(.hdr_omega[que_head],DELINK(.L[itm_llink]))
End
End;
! turn off all alpha bits now (but what about entries which were
! removed from the alpha list?)
L = .hdr_alpha[que_head];
While (L = .L[itm_rlink]) Neqa .hdr_alpha[que_head] Do
Begin
NODE = .L[itm_int_rdata(1)];
NODE[gt_v_alpha] = FALSE
End
End;
! CALLED AT END OF FORKED CONTROL STRUCURE TO COMPUTE ALPHA,
! OMEGA LISTS AS WELL AS GENERATE BOGUS NODES FOR THOSE
! C-S-E'S WHICH WERE MADE AVAILABLE BY FORKED EXPRESSIONS.
Routine GPOSTFORK : Novalue =
Begin
Local
NODE : Ref GT,
ALLGT : Boolean;
If .swit_quick Then
Return;
! if too few entries
If (.TOS - .LASTMARK) Lss 5 Then
Return;
hdr_alpha = .STK[.LASTMARK+1];
hdr_omega = .STK[.TOS];
! quick scan to see if all operands are expressions. a non-expression
! guarantees the alpha and omega lists will be null
ALLGT = (
Incr I From .LASTMARK+3 To .TOS-1 Do
Begin
NODE = .STK[.I];
If .NODE[gt_type] Neq T_NODE Then
Exitloop 0
End);
If .ALLGT Then
Begin
GENALPHA();
GENOMEGA();
GENPSI();
GCSEFROMPSI()
End
End;
! CALLED FROM: GENPSI
! VERY SIMILAR IN PURPOSE, STRUCTURE TO SORTFINT (SEE LSTPKG).
! PURPOSE: "GROWS" PSI LIST AND PCS CHAINS
! ARGUMENT: NXTHDR - HEADER OF AN EPILOG LIST
! LOCALS:
! PPSI - CURRENT LIST ENTRY FROM PSI LIST
! PNXT - CURRENT LIST ENTRY FROM EPILOG LIST (NXTHDR)
! VALPSI,VALNXT - EPILOG LISTS ARE SORTED BY THEIR 'ITEMFPARENT'
! FIELDS, AND THESE ARE THE 'ITEMFPARENT'S OF
! PPSI AND PNXT, RESPECTIVELY.
!
Routine PSIINT(NXTHDR : Ref LSTHDR) : Novalue =
Begin
Local
PPSI : Ref ITEM,
PNXT : Ref ITEM,
VALPSI : Ref GT,
VALNXT : Ref GT,
NL : Ref GT,
L : Ref GT;
Routine PSIENTER(I : Ref GT,T : Ref GT,V : Ref GT) : Novalue =
Begin
Local
NI : Ref GT;
If Not .I[gt_v_csp] Then
I = .I[gt_csparent];
Do
Begin
NI = .I[gt_pcsthread];
Do
Begin
If .T Eqla .I Then
Exitloop;
If .T[gt_pcsthread] Eqla 0 Then
Begin
T[gt_pcsthread] = .I;
I[gt_pcsthread] = 0;
T = .V;
Exitloop
End;
T = .T[gt_pcsthread]
End
While 1
End
Until (I = .NI) Eql 0
End;
Macro
UDPSI= ! GET NEXT PSI,VALPSI
Begin
PPSI = .PPSI[itm_rlink];
VALPSI = .PPSI[itm_fparent]
End %;
Macro
UDNXT= ! GET NEXT PNXT,VALNXT
Begin
PNXT = .PNXT[itm_rlink];
If .PNXT Eqla .NXTHDR Then
VALNXT = 0
Else
VALNXT = .PNXT[itm_fparent]
End %;
PPSI = .hdr_psi;
PNXT = .NXTHDR;
UDPSI;
UDNXT;
While .PPSI Neqa .hdr_psi Do
Begin
If .VALPSI Eqla .VALNXT Then
! ADD A NEW ENTRY TO THE PSI LIST
Begin
PSIENTER(.PNXT[itm_rdata(1)],.PPSI[itm_rdata(1)],
.PPSI[itm_rdata(1)]);
UDPSI;
UDNXT
End
Else If .VALPSI Gtr .VALNXT Then
!
! NO FORMAL COPY OF THE NODE POINTED TO BY PPSI IS ON THE
! EPILOG LIST (POINTED TO BY NXTHDR). THE PCSTHREAD CHAIN
! THAT HAS BEEN BUILT HANGING OFF THAT NODE IS BROKEN; NOTE
! THAT IF SOME NODE 'L' ON THAT CHAIN IS ITSELF 'BOGUS', I.E.
! HAS AN ALREADY-BUILT PCS CHAIN OF ITS OWN THAT MUST NOT BE
! BROKEN DURING THIS PROCESS, L'S 'END OF PCS' FIELD POINTS
! TO THE END OF THAT CHAIN.
!
Begin
Do
Begin
L = .PPSI[itm_rdata(1)];
While .L Neqa 0 Do
Begin
If .L[gt_v_bogus] And .L[gt_end_of_pcs] Neqa 0 Then
L = .L[gt_end_of_pcs];
NL = .L[gt_pcsthread];
L[gt_pcsthread] = 0;
L = .NL
End;
UDPSI;
RELITEM(.PPSI[itm_llink],SZ_PSI_ITEM);
If .PPSI Eqla .hdr_psi Then
Return
End
Until .VALPSI Leq .VALNXT
End
Else
Begin
Do
UDNXT
Until .VALNXT Leq .VALPSI
End
End
End;
Routine GENPSI : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
LSTPTR = .Block[.STK[.LASTMARK+3],gt_flow];
hdr_psi = .LSTPTR[flow_epilog];
LSTPTR[flow_epilog] = 0;
Incr I From .LASTMARK+4 To .TOS-1 Do
Begin
LSTPTR = .Block[.STK[.I],gt_flow];
PSIINT(.LSTPTR[flow_epilog]);
FreeList(.LSTPTR[flow_epilog]);
LSTPTR[flow_epilog] = 0
End
End;
!
! CALLED FROM: G CSE FROM PSI
! ASSUMES THAT FORMER IS THE FORMAL PARENT OF BOGUS,
! AND THAT .FORMER[FSTHREAD] == .BOGUS; CAUSES THE TWO
! NODES TO SWITCH PLACES IN THE GT HASH TABLE.
!
Routine CHANGEFPAR(FORMER : Ref GT,BOGUS : Ref GT) : Novalue =
Begin
Local
L : Ref GT,
M : Ref GT;
! locate where on the GTHASH chain the new formal parent should go
L = .GTHASH[.FORMER[gt_code]];
If .L Eqla .FORMER Then
GTHASH[.FORMER[gt_code]] = .BOGUS
Else
Begin
Until .L[gt_gthread] Eqla .FORMER Do
L = .L[gt_gthread];
L[gt_gthread] = .BOGUS
End;
! give the GTTHREAD to the bogus node and make former a member
! of bogus's formal thread
BOGUS[gt_gthread] = .FORMER[gt_gthread];
FORMER[gt_fsthread] = .BOGUS[gt_fsthread];
BOGUS[gt_fsthread] = .FORMER;
! give a new formal parent to all the entries
L = .BOGUS;
Do
Begin
M = .L;
Do
M[gt_fparent] = .BOGUS
Until (M = .M[gt_csthread]) Eqla 0
End
Until (L = .L[gt_fsthread]) Eqla 0
End;
Routine GCSEFROMPSI : Novalue =
Begin
Local
L : Ref ITEM,
BOGNODE : Ref GT,
FNODE : Ref GT,
CNODE : Ref GT,
M : Ref GT,
I : Ref ITEM,
X : Ref GT;
Label
aaa;
! loop for each Psi list entry
L = .hdr_psi;
While (L = .L[itm_rlink]) Neq .hdr_psi Do
aaa: Begin
CNODE = .L[itm_rdata(1)] And Not 1;
! catch (and throw out) nodes that were created before the fork, and
! were not invalidated on any branch. they would have been created
! before the fork if they haven't been invalidated yet.
If Not .CNODE[gt_v_purge] And Not .CNODE[gt_v_rm] Then
Leave aaa;
! catch (and throw out) nodes of the form '.variable'. these are too
! simple to move.
If .CNODE[gt_code] Eql OP_DOT And Not .CNODE[gt_v_bogus] Then
Begin
X = .CNODE[gt_arg1];
If .X[gt_type] Neq T_NODE Then
Leave aaa
End;
! catch (and throw out) nodes on the current alpha list. if they
! are already moving out the front then there is no sense in moving
! them out the back.
I = .hdr_alpha[que_head];
Until (I = .I[itm_rlink]) Eql .hdr_alpha[que_head] Do
Begin
M = .CNODE;
Do
Begin
Decr J From .I[itm_size] To 1 Do
If .M Eql .I[itm_int_rdata(.J)] Then
Leave aaa
End
Until (M = .M[gt_pcsthread]) Eql 0
End;
! create a bogus node to represent this Psi list entry
Mark();
FNODE = .L[itm_fparent];
BOGNODE = MAKGT(-.FNODE,.FNODE[gt_code]);
!
! 'FPARSEARCH' MUST ENCOUNTER 'BOGNODE' BEFORE IT ENCOUNTERS
! ANY OF THE BRANCH NODES; THEREFORE, THE FOLLOWING CHECK IS
! MADE, AND IF ANY OF THE BRANCH NODES IS FORMAL PARENT OF THE
! REST OF THEM, IT CHANGES PLACE IN THE GT-HASH TABLE WITH
! 'BOGNODE'.
!
M = .CNODE;
Do
If .M Eqla .FNODE Then
Begin
CHANGEFPAR(.M,.BOGNODE);
Exitloop
End
Until (M = .M[gt_pcsthread]) Eqla 0;
! add to the bogus list
ENLST(.hdr_bogus[stk_data],MAKITEM(.BOGNODE));
! give it all the characteristics of a bogus node
BOGNODE[gt_v_bogus] = TRUE;
BOGNODE[gt_occ] = 0;
! tack the Psi list onto the bogus node and also note the end of the list
BOGNODE[gt_pcsthread] = .CNODE;
FNODE = .CNODE;
Until .FNODE[gt_pcsthread] Eqla 0 Do
FNODE = .FNODE[gt_pcsthread];
BOGNODE[gt_end_of_pcs] = .FNODE;
! lower the level of the head of the Psi list so it look usable
CNODE[gt_crlevel] = .LEVEL;
CNODE[gt_v_purge] = FALSE;
CNODE[gt_v_rm] = FALSE;
CNODE[gt_v_mm] = FALSE
End;
FreeList(.hdr_psi)
End;
!
! CALLED FROM: BIND LOOP CSE
! ARGUMENT: NODE - A GT NODE WITHIN THE CURRENT LOOP
! VALUE RETURNED: IF THE NODE HAS A CSE PARENT OUTSIDE THE LOOP,
! RETURN A POINTER TO THE CSE PARENT; OTHERWISE 0.
!
Routine FINDPRELOOPCSE(NODE : Ref GT) =
Begin
Local
L : Ref GT,
Q : Ref LVL,
NEXTFLOOR : Integer,
THISFLOOR : Integer;
! loop for a CSE between the current floor and the previous floor
THISFLOOR = .FLOOR[stk_data];
Q = .FLOOR[stk_next];
NEXTFLOOR = .Q[stk_data];
L = .NODE[gt_fparent];
Do
! if not invalidated and created above the lower floor then we found it
If Not .L[gt_v_rm] And Not .L[gt_v_purge] And .L[gt_crlevel] Lss .THISFLOOR Then
If .L[gt_crlevel] Geq .NEXTFLOOR Then
Return .L
While (L = .L[gt_fsthread]) Neqa 0;
Return 0
End;
!
! CALLED FROM: BIND LOOP CSE
! ARGUMENT: X - A GT NODE IN THE CURRENT LOOP, FOR WHICH A
! CSE PARENT HAS JUST BEEN FOUND OUTSIDE THE LOOP.
! PURPOSE: TAKE X OFF THE PROLOG OF THE CURRENT LINEAR BLOCK (IF IT'S ON).
!
! notes
! 'X' has just been found to be a CSE outside the loop. it thus
! does not belong in the prolog of the current block
!
Routine REMOVEFROMPRLG(X : Ref GT) : Novalue =
Begin
Local
FPAR : Ref GT,
L : Ref ITEM;
FPAR = .X[gt_fparent];
L = .hdr_prolog[stk_data];
While (L = .L[itm_rlink]) Neqa .hdr_prolog[stk_data] Do
If .L[itm_fparent] Eqla .FPAR Then
Return RELITEM(.L,SZ_ITEM(1))
End;
!
! CALLED FROM: GPOSTWDW, GPOSTREP
! PURPOSE:
! FOR EVERY NODE CREATED IN THE CURRENT LOOP, TRY TO FIND
! A CSPARENT OUTSIDE THE LOOP, AND IF IT IS FOUND, RESET
! ALL THE APPROPRIATE 'CSTHREAD','CSPARENT',ETC. FIELDS.
!
! notes
! CSE recognition is disabled across the floor of a loop.
! for those expression created in the loop but not invalidated
! a check is made for a matching CSE outside the loop and
! if found, the CSE inside the loop is bound to it.
!
Routine BINDLOOPCSE : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
LC : Ref GT,
L1 : Ref GT,
A : Integer,
F : Integer,
M : Ref GT;
Label
aaa,bbb,ccc;
If .swit_quick Then
Return;
F = .FLOOR[stk_data];
A = .ABCBASE[stk_data];
! loop for all CSE's
FORALLRATORS(I)
Begin
LFP = .GTHASH[.I];
While .LFP Neq 0 Do
Begin
M = .LFP;
While (M = L = .M[gt_fsthread]) Neq 0 Do
ccc: Begin
! if created above the floor (e.g. inside the loop) and in this
! linear block and not purged...
If .L[gt_crlevel] Lss .F Then
Leave ccc;
If .L[gt_abc] Lss .A Then
Leave ccc;
If .L[gt_v_rm] Then
Leave ccc;
! if there is a CSE outside the loop which it is a CSE of...
LC = FINDPRELOOPCSE(.L);
If .LC Eqla 0 Then
Leave ccc;
! if an unbound Psi CSE then bind it
If .LC[gt_v_bogus] And .LC[gt_occ] Eql 0 Then
BINDPCSTHREAD(.LC);
! if a Psi list parent...
If .L[gt_v_bogus] Then
Begin
L[gt_v_rm] = TRUE;
L[gt_mklevel] = 0;
! ... with CSE's off it (if bogus, how could it not have children? if
! it was split up earlier?)
If .L[gt_occ] Gtr 0 Then
Begin
! attach all the entries to their new CSE parent
L1 = .L[gt_csthread];
Do ! RESET 'CSPARENT' FIELDS OF CSE USES
Begin
L1[gt_csparent] = .LC;
L1[gt_depth] = .LC[gt_xdepth];
L1[gt_v_mustgencode] = FALSE;
If .L1[gt_csthread] Eqla 0 Then
Exitloop;
L1 = .L1[gt_csthread]
End
While TRUE;
! attach them to the CSE list
L1[gt_csthread] = .LC[gt_csthread];
LC[gt_csthread] = .L[gt_csthread];
L[gt_csthread] = 0;
LC[gt_occ] = .LC[gt_occ]+.L[gt_occ];
DECROCC(.LC)
End
End
Else ! if .LC[BOGUSBIT]
! if not bogus
Begin
L1 = .L;
Do ! RESET 'CSPARENT' FIELDS OF CSE USES
Begin
L1[gt_csparent] = .LC;
If .L1[gt_csthread] Eqla 0 Then
Exitloop;
L1 = .L1[gt_csthread];
L1[gt_depth] = .LC[gt_xdepth]
End
While TRUE;
L[gt_v_mustgencode] = FALSE;
aaa: Begin ! PUT 'L' AT END OF CSE CHAIN OF 'LC'
L1 = .LC;
While .L1[gt_csthread] Neqa 0 Do
If .L1[gt_csthread] Eqla .L Then
Leave aaa
Else
L1 = .L1[gt_csthread];
L1[gt_csthread] = .L
End;
L1 = .LFP;
bbb: Begin ! TAKE 'L' OFF CHAIN OF CSE PARENTS
While .L1[gt_fsthread] Neqa .L Do
If (L1 = .L1[gt_fsthread]) Eqla 0 Then
Begin
L1 = .L;
Leave bbb
End;
L1[gt_fsthread] = .L[gt_fsthread];
L[gt_fsthread] = 0
End;
LC[gt_occ] = .LC[gt_occ]+.L[gt_occ];
DECROCC(.LC);
M = .L1
End;
REMOVEFROMPRLG(.L);
L[gt_depth] = .LC[gt_xdepth]
End;
LFP = .LFP[gt_gthread]
End
End
End;
! test whether a node from a loop prolog may be placed on a Chi list
! (e.g. is loop invariant.)
!
! notes:
! here is the reason why entries may point to alpha lists
! rather than items. it is because all the items on the
! alpha list must be checked.
!
! see page 31 of "The Design of an Optimizing Compiler".
!
! "Given a loop control environment, the set of loop invariant
! expressions is described by: X = prolog(B) /\ epilog(B),
! where B is the linear block relative to the compound
! expression (B1;B2) in the "do-while" case and (B2;B1) in the
! "while-do" case."
Routine ISCHI(I : Ref ITEM) =
Begin
Bind
NODE = I : Ref GT;
Local
P : Ref GT;
! if this is an alpha list entry then it is a Chi entry if all the
! items on the alpha list may be on the Chi list
If .I<0,1> Then
Begin
I<0,1> = 0;
Decr J From .I[itm_size] To 1 Do
If Not ISCHI(.I[itm_int_rdata(.J)]) Then
Return FALSE
End
Else
! for regular expressions, an item is available for the Chi list if
! the node has not been killed and its CSE parent has not been killed
Begin
If .NODE[gt_v_rm] Or .NODE[gt_v_purge] Then
Return FALSE;
p = .NODE[gt_csparent];
If .p[gt_v_rm] Or .p[gt_v_purge] Then
Return FALSE
End;
Return TRUE
End;
!
! VALUE: IF Z AND LFP TOGETHER BELONG ON RHO LIST OF CURRENT LOOP,
! RETURN LFP; IF NO SUCH LFP CAN BE FOUND, RETURN 0.
!
! notes:
! this routine determines whether an expressions is cyclically
! re-evaluated.
!
! 'L' had previously been discovered to be invalidated by ISCHI above.
!
! this routine searches for a later instance of the expression
! 'L' which is still valid in the loop. if found, it will
! supply the next valid value for 'L'.
!
! see page 32 of "The Design of an Optimizing Compiler".
!
! "Given a loop control environment where B is the linear block
! relative to the expression (B1;B2) ("do-while") or (B2;B1)
! ("while-do"), the set of expressions whose evaluations at
! the head of B are redundant to evaluations at the tail of B
! are described by the set: P = prolog(B) /\ epilog(B)."
Routine ENRHO(L : Ref ITEM) =
Begin
Local
LFP : Ref GT,
Z : Ref GT;
! simple dotted variables never go on the Rho list
Z = .Block[NONBOGUS(.LFP),gt_arg1];
If .LFP[gt_code] Eql OP_DOT And .Z[gt_type] Eql T_VARIABLE Then
Return 0;
! look for a CSE parent which has not been invalidated and was created
! in this loop
LFP = .L[itm_fparent];
Do
If Not .LFP[gt_v_rm] And Not .LFP[gt_v_purge] Then
If .LFP[gt_crlevel] Geq .FLOOR[stk_data] Then
Return .LFP
While (LFP = .LFP[gt_fsthread]) Neqa 0;
Return 0
End;
! GENERATE THE CHI AND RHO LISTS FOR A LOOP CONTROL CONSTRUCT
Routine GENCHIRHOLST : Novalue =
Begin
Local
L : Ref ITEM,
HDR : Ref LSTHDR,
NODE : Ref GT,
RANDLEX : Ref GT,
LC : Ref ITEM,
L2 : Ref ITEM;
! loop, selecting items from the prolog to place on either the
! Chi list or maybe the Rho list
!
! note: this code is similar to the code in GENALPHA
L = HDR = .hdr_prolog[stk_data];
While (L = .L[itm_rlink]) Neqa .HDR Do
! try first to put it on the Chi list
If ISCHI(.L[itm_rdata(1)]) Then
ENLST(.hdr_chi[que_head], MAKITEM(.L[itm_data(1)]))
! try second to put it on the Rho list
Else If (LC = ENRHO(.L)) Neqa 0 Then
ENLST(.hdr_rho[que_head], MAKITEM(.L[itm_data(1)],.LC));
! convert pointers to other alpha lists to real nodes
HDR = L = .hdr_chi[que_head];
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
LC = .L[itm_rdata(1)];
! if an alpha list entry rather than an item, resolve to the real entry
While .LC<0,1> Do
Begin
LC<0,1> = 0;
LC = .LC[itm_int_rdata(1)]
End;
L[itm_rdata(1)] = .LC
End;
! the same for the Rho list
HDR = L = .hdr_rho[que_head];
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
LC = .L[itm_rdata(1)];
While .LC<0,1> Do
Begin
LC<0,1> = 0;
LC = .LC[itm_int_rdata(1)]
End;
L[itm_rdata(1)] = .LC
End;
! mark all children of Rho list entries as Rho children.
L = .HDR;
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
NODE = .L[itm_rdata(1)];
FORALLRANDS(I,.NODE)
Begin
RANDLEX = .NODE[gt_argv(.I)];
If .RANDLEX[gt_type] Eql T_NODE Then
RANDLEX[gt_v_rho] = TRUE
End
End;
! now remove the children marked above from the Rho list
L = .HDR[itm_rlink];
While .L Neq .HDR Do
Begin
NODE = .L[itm_rdata(1)];
L2 = .L;
L = .L[itm_rlink];
If Not .NODE[gt_v_rho] Then
Begin
L2[itm_abc] = .NODE[gt_abc];
! reverse the order of the Rho list
ENLST(.HDR,DELINK(.L2))
End
Else
Begin
NODE = .L2[itm_rdata(2)];
NODE[gt_v_rho] = TRUE;
RELITEM(.L2,SZ_RHO_ITEM)
End;
End;
End;
!
! CALLED FROM: F16, F17, F18
! SEE GALPHATOPRLG
!
! notes:
! this routine moves Chi list entries to the prolog. it
! also makes sure that a child of a Chi entry is not on
! the Chi list with its parent.
Routine GCHITOPRLG(chi_level : Integer) : Novalue =
Begin
Local
L : Ref ITEM,
HDR : Ref LSTHDR,
NODE : Ref GT,
HI : Integer,
RANDLEX : Ref GT,
L2 : Ref ITEM;
If .NOTREE Then
Return;
! loop for each Chi entry. if not killed, add it to the prolog list
HI = .hdr_chi[que_abc];
L = HDR = .hdr_chi[que_head];
While (L = .L[itm_rlink]) Neqa .HDR Do
If GALOMBITS(.HI,.ABCBASE[stk_data],.L[itm_fparent]) Then
ENLST(.hdr_prolog[stk_data],MAKITEM(.L[itm_data(1)]));
L = .HDR;
! now mark all the children of Chi list entries
!
! Q: why was this not done above?
!
! A: maybe to emphasize that the above loop moved all entries on the
! Chi listing including children of other Chi entries.
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
NODE = .L[itm_rdata(1)];
FORALLRANDS(I,.NODE)
Begin
RANDLEX = .NODE[gt_argv(.I)];
If .RANDLEX[gt_type] Eql T_NODE Then
RANDLEX[gt_v_chi] = TRUE
End
End;
! REVALIDATE ALL NODES ON THE CHI LIST. ALSO SEE OPENWUCSE
! if a node is a child of another entry on the Chi list then remove it
L = .HDR[itm_rlink];
While .L Neqa .HDR Do
Begin
NODE = .L[itm_rdata(1)];
NODE[gt_crlevel] = .chi_level;
NODE[gt_v_jm] = FALSE;
NODE[gt_v_rm] = FALSE;
NODE[gt_v_mm] = FALSE;
NODE[gt_v_purge] = FALSE;
L2 = .L;
L = .L[itm_rlink];
! if a child of a Chi entry then remove it. don't want both
! parents and children on the same list.
If .NODE[gt_v_chi] Then
RELITEM(.L2,SZ_CHI_ITEM)
Else
Begin
L2[itm_abc] = .NODE[gt_abc];
! reverse the order of the Chi list
ENLST(.HDR,DELINK(.L2))
End
End
End;
!
! CALLED FROM: F16, F18
! CALLED WHEN: AFTER WHILE-DO,UNTIL-DO,DO-WHILE,DO-UNTIL LOOP
! PURPOSE:
! TAKE ALL NODES THAT 1. WERE CREATED IN THE LOOP
! 2. WERE NOT INVALIDATED AFTER CREATION
! 3. MUST BE EXECUTED AT LEAST ONCE
! (I.E. FOR A WHILE-DO LOOP, THE EPILOGUE LIST OF THE WHILE PART;
! FOR A DO-WHILE LOOP, THE EPILOGUE LIST OF THE ENTIRE LOOP)
! (N.B. THE SAME LISTS THAT WERE CREATED BY GFWHILE,GFDOWHILE)
! AND REVALIDATES THE NODES, LOWERING THEIR CRLEVEL VALUES
! TO MAKE THEM LOOK AS IF THEY WERE CREATED OUTSIDE THE LOOP.
! ARGUMENT: WHICHTYPE - TRUE FOR DO-WHILE,DO-UNTIL
! FALSE FOR WHILE-DO,UNTIL-DO
!
! notes:
! this routine exposes redundant expressions to their
! surrounding environment.
!
! see page 32-33 of "The Design of an Optimizing Compiler".
!
Routine OPENWUCSE(WHICHTYPE : Boolean) : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR,
NODE : Ref GT,
L : Ref ITEM,
HDR : Ref LSTHDR,
LCP : Ref GT;
! if tree generation turned off...
If .NOTREE Then
Return;
! if DO...
If .WHICHTYPE Then
Begin
NODE = .SYM[gt_arg4]; ! DO condition
If .NODE[gt_type] Neq T_NODE Then
NODE = .SYM[gt_arg3] ! DO body
End
Else
! if WHILE...
NODE = .SYM[gt_arg3]; ! WHILE condition
! return if the condition/body is not a null because it would not
! have an epilog set
If .NODE[gt_type] Neq T_NODE Then
Return;
! loop for each epilog entry
LSTPTR = .NODE[gt_flow];
L = HDR = .LSTPTR[flow_epilog];
While (L = .L[itm_rlink]) Neqa .HDR Do
Begin
LCP = .L[itm_rdata(1)];
! loop for each CSE parent
Do
Begin
! if the CSE parent was not purged and created in this loop then
! lower its creation level and make it look valid outside the loop.
LCP = .LCP[gt_csparent];
If Not .LCP[gt_v_purge] And .LCP[gt_crlevel] Geq .LEVEL Then
Begin
If .LCP[gt_v_rm] Then
LCP[gt_v_mm] = TRUE;
LCP[gt_v_rm] = FALSE;
LCP[gt_crlevel] = .LEVEL;
LCP[gt_xdepth] = .LOOPDEPTH;
Exitloop
End
End
While (LCP = .LCP[gt_fsthread]) Neqa 0
End;
! all done with the epilog list now
FreeList(.HDR);
LSTPTR[flow_epilog] = 0
End;
!
! CALLED FROM: F16, F18
! CALLED WHEN: A WHILE OR UNTIL LOOP HAS BEEN PARSED
!
Routine GPOSTWDW : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
If .NOTREE Then
Return;
! find any expressions within the loop which have not been invalidated
! and have a CSE outside the loop and bind them together.
BINDLOOPCSE();
! form the Chi and Rho lists
hdr_chi = .SYM[gt_arg2];
hdr_rho = .SYM[gt_arg1];
GENCHIRHOLST();
! we can release the prologue now.
FreeList(.hdr_prolog[stk_data]);
If .SYM[gt_type] Eql T_NODE Then
Begin
LSTPTR = .SYM[gt_flow];
LSTPTR[flow_prolog] = 0
End
End;
!
! CALLED BY: F25
! CALLED WHEN: A LABELED EXPRESSION HAS BEEN PARSED
! PURPOSE:
! REMOVE TYPE 1 KILLS CAUSED BY "LEAVE"S TO THAT LABEL
! FROM THE KILL LIST.
!
! notes:
! LEAVE's block alpha and omega code motion. when the
! block the LEAVE is contained in goes out of scope then
! the blockage is removed.
Routine REMOVELEAVEKILLS(LABNODE : Ref GT) : Novalue =
Begin
Local
I : Ref ITEM,
I2 : Ref ITEM,
NODE : Ref GT;
I = .hdr_kill;
I = .I[itm_rlink];
While .I Neqa .hdr_kill Do
Begin
I2 = .I;
I = .I[itm_rlink];
If .I2[itm_kill_abc] Lss .ABCOUNT Then
Return;
NODE = .I2[itm_kill_cause];
If .I2[itm_kill_type] Eql 1 And .NODE[gt_arg2] Eqla .LABNODE[gt_arg2] Then
RELEASESPACE(DELINK(.I2),SZ_ITEM(1))
End
End;
!
! I'M NOT SURE THERE'S ANY JUSTIFICATION FOR THIS ROUTINE.
! CALLED BY GPOSTREP; SETS THE CKF FIELD OF THE REQUEST
! WORD PASSED (IN "DELAY") TO THE 'BY' OR 'TO' PARTS OF
! AN INCR-DECR LOOP.
!
! notes
! this routines notes whether the TO and BY parts are
! invalidated within the loop. If they are then they
! most certainly have to be placed in a temporary. If
! not then delay may choose to use the value directly.
Routine BYTOCHK(N : Ref GT) =
Begin
Macro
FLD_K_OPERAND = 1^57 %,
FLD_K_TEMP = 3^57 %; ! CAUTION, COPIED FROM DELAY
If .N[gt_type] Neq T_NODE Then
Return .N+FLD_K_OPERAND
Else If .N[gt_v_rm] Eql 0 And .N[gt_v_mm] Eql 0 Then
Return .N+FLD_K_OPERAND
Else
Return .N+FLD_K_TEMP
End;
!
! CALLED FROM: F17
! CALLED WHEN: AN INCR-DECR LOOP HAS BEEN PARSED
!
Routine GPOSTREP : Novalue =
Begin
Local
LSTPTR : Ref FLOLSTPTR;
If .NOTREE Then
Return;
! find any expressions not invalidated within the loop and
! which are CSE's outside the loop and bind them together.
BINDLOOPCSE();
! give information to DELAY as to whether the loop increment
! and limit expressions were invalidated within the loop.
STK[.TOS-3] = BYTOCHK(.STK[.TOS-3]);
STK[.TOS-4] = BYTOCHK(.STK[.TOS-4]);
! generate the Chi and Rho lists and release the prolog afterward
hdr_chi = .STK[.TOS-1];
hdr_rho = .STK[.TOS-2];
GENCHIRHOLST();
FreeList(.hdr_prolog[stk_data]);
! note that this node no longer has a valid prolog list attached to it
!
! Q: how can sym not be a node?
If .SYM[gt_type] Eql T_NODE Then
Begin
LSTPTR = .SYM[gt_flow];
LSTPTR[flow_prolog] = 0
End
End;
! called by:
! SELECT pre-e2
! LEAVE pre-e0
! EXITLOOP pre-e0
! ENABLE
Global Routine F0 : Novalue =
Begin
PUSHANDBUMP(CEILING)
End;
! called by:
! DO pre-e1
Global Routine F1 : Novalue =
Begin
PUSHANDBUMP(FLOOR);
PUSHFLO();
PushQueue(T_RHO);
PushQueue(T_CHI)
End;
! called by:
! RETURN post-e0
Global Routine F2 : Novalue =
Begin
KILL(0,.SYM)
End;
! called by:
! INLINE post-e0
Global Routine F3 : Novalue =
Begin
KILL(5,.SYM);
MARKALL(TRUE)
End;
! called by:
! IF post-e1
! IF post-e2
! CASE post-e1
Global Routine F4 : Novalue =
Begin
MARKMMNODES();
GFBRANCH();
POPFLO();
REFRESH()
End;
! called by:
! IF pre-make
! CASE pre-make
Global Routine F5 : Novalue =
Begin
PushQueue(T_OMEGA);
MARKUPDATE();
POPANDDUMP(CEILING);
MARKMMNODES();
GPOSTFORK()
End;
! called by:
! INCR pre-make
Global Routine F6 : Novalue =
Begin
PURGE();
MARKUPDATE();
POPANDDUMP(CEILING);
POPANDDUMP(FLOOR)
End;
! called by:
! SELECT post-e2
! ENABLE post-e2
Global Routine F7 : Novalue =
Begin
PURGE();
MARKUPDATE();
POPANDDUMP(CEILING);
MARKMMNODES()
End;
! called by:
! IF pre-e1
! IF pre-e2
! CASE pre-e1
Global Routine F8 : Novalue =
Begin
PUSHFLO()
End;
! called by:
! SINCR between initial, low, and high values
! CALL between each argument
! SELECT
! CREATE between each argument
! SIF after creating a node for a literal condition
! DO after parsing the loop condition
Global Routine F9 : Novalue =
Begin
MARKMMNODES()
End;
! called by:
! SINCR before parsing the loop body
Global Routine F10 : Novalue =
Begin
PUSHANDBUMP(FLOOR);
PUSHANDBUMP(CEILING);
ENTVUSELST(.STK[.LASTMARK+1],0);
ENTVCHGLST(.STK[.LASTMARK+1],0);
PUSHFLO();
PushQueue(T_RHO);
PushQueue(T_CHI)
End;
! called by:
! CALL and CREATE after creating a CALL node
!
! note: CREATE is really a special form of call.
Global Routine F11 : Novalue =
Begin
Local
L : Ref GT,
LFP : Ref GT,
B : Ref GT,
Q : Ref GT;
Routine MLST(L : Ref ST) : Novalue =
Begin
Do
MRK(L)
While (L = .L[gt_fsthread]) Neqa 0
End;
If .swit_quick Then
Return;
If .NOTREE Then
Return;
! look for arguments which are the addresses of names and mark those
! names as being both referenced and modified.
FORALLRANDS(I,.SYM)
Begin
Q = FINDNAME(.SYM[gt_argv(.I)]);
If .Q Neqa 0 Then
Begin
MRKDOTNODES(.Q);
ENTVCHGLST(.Q,.SYM);
ENTVUSELST(.Q,.SYM);
End
End;
! next, mark all non-local variables (e.g. variables which the
! called routine may modify) which have been dotted.
LFP = .GTHASH[OP_DOT];
While .LFP Neqa 0 Do
Begin
B = NONBOGUS(.LFP);
Q = FINDNAME(.B[gt_arg1]);
If .Q Neqa 0 Then
Begin
If Not .Q[st_v_nouplevel] And ISSTVAR(Q) Then
MLST(.LFP)
End
Else If .swit_mark Then
MLST(.LFP);
LFP = .LFP[gt_gthread]
End;
! insert a kill so loads and store may not move past the call
KILL(4,.SYM)
End;
! called by:
! RNAMESFOLLOWS before parsing a routine body
!
! notes
! this routine saves the current flow context for a nested
! routine declaration.
Global Routine F12 : Novalue =
Begin
Local
L : Ref Vector;
PUSHFLO();
PUSHLV(hdr_bogus,SZ_LVL);
hdr_bogus[stk_data] = MAKHDR(BOGREMOVE,BOGENTER);
If .swit_quick Then
Return;
L = GETSPACE(MAXDELIMITER+2);
MOVECORE(GTHASH,.L,MAXDELIMITER+1);
CLEARCORE(GTHASH,MAXDELIMITER+1);
L[MAXDELIMITER+1] = .FOUNDATION;
FOUNDATION = .L
End;
! called by:
! RNAMEFOLLOWS after creating a routine body node
Global Routine F13 : Novalue =
Begin
Local
L1 : Ref Vector,
L : Ref ST,
E : Ref ITEM,
Q : Ref LSTHDR;
FreeList(.hdr_prolog[stk_data]);
POPFLO();
ABCOUNT = .ABCBASE[stk_data];
! release all kill items created in the current routine
Q = .hdr_kill;
Until (E = .Q[itm_rlink]) Eqla .hdr_kill Do
Begin
If .E[itm_kill_abc] Leq .ABCOUNT Then
Exitloop;
RELEASESPACE(DELINK(.E),SZ_ITEM(1))
End;
If .swit_quick Then
Return;
! restore the GT hash chains
L1 = .FOUNDATION;
FOUNDATION = .L1[MAXDELIMITER+1];
MOVECORE(.L1,GTHASH,MAXDELIMITER+1);
! release all the use/change list entries created for this routine
Decr J From HTSIZE-1 To 0 Do
Begin
L = .HT_THREAD[.J];
While .L Neqa 0 Do
Begin
If ISSTVAR(L) Then
If .L[st_var_chg_list] Neqa 0 Then
Decr I From 1 To 0 Do
Begin
Q = (If .I
Then .L[st_var_use_list]
Else .L[st_var_chg_list]);
While (E = .Q[itm_rlink]) Neqa .Q Do
Begin
If .E[itm_abc] Leq .ABCOUNT Then
Exitloop;
RELEASESPACE(DELINK(.E),SZ_ITEM(1))
End
End;
L = .L[st_next]
End
End;
RELEASESPACE(.L1,MAXDELIMITER+2)
End;
Global Routine CLEANUPFLOW : Novalue =
Begin
If .num_error Eql 0 Then
Begin
FreeList(.hdr_bogus[stk_data]);
POPLV(hdr_bogus,SZ_LVL)
End
End;
! called by:
! SOPERATOR for AND/OR and by SEXITLOOP after parsing the leave
! expression.
Global Routine F14 : Novalue =
Begin
PURGE();
MARKUPDATE();
POPANDDUMP(CEILING)
End;
! called by:
! SIF after parsing the conditional expression
Global Routine F15 : Novalue =
Begin
MARKMMNODES();
PUSHANDBUMP(CEILING)
End;
! called by:
! SDO after creating the DO node
Global Routine F16 : Novalue =
Begin
GFLOOP();
GPOSTWDW();
POPANDDUMP(FLOOR);
POPFLO();
GCHITOPRLG(.LEVEL);
OPENWUCSE(TRUE)
End;
! called by:
! SINCR after parsing the loop body
Global Routine F17 : Novalue =
Begin
Local
L : Ref LVL;
GFLOOP();
MARKMMNODES();
GPOSTREP();
POPFLO();
L = .LVLCOPY[stk_next];
L = .L[stk_next];
GCHITOPRLG(.L[stk_data])
End;
! called by:
! SWU after creating the while node
Global Routine F18 : Novalue =
Begin
GFLOOP();
GPOSTWDW();
PURGE();
MARKUPDATE();
POPANDDUMP(CEILING);
POPANDDUMP(FLOOR);
POPFLO();
GCHITOPRLG(.LEVEL);
OPENWUCSE(FALSE)
End;
! called by:
! SWU after parsing the while condition
Global Routine F19 : Novalue =
Begin
MARKMMNODES();
PUSHANDBUMP(CEILING);
GFWHILE()
End;
! called by:
! SIF before the conditional expression is parsed
Global Routine F20 : Novalue =
Begin
PushQueue(T_ALPHA)
End;
! called by:
! SCOMPOUND after creating an enable block label
Global Routine F21 : Novalue =
Begin
MARKALL(TRUE)
End;
! called by:
! ENABLE pre-e0
Global Routine F22 : Novalue =
Begin
PUSHANDBUMP(CEILING);
PUSHANDBUMP(FLOOR)
End;
! called by:
! ENABLE post-make
Global Routine F23 : Novalue =
Begin
PURGE();
MARKUPDATE();
POPANDDUMP(CEILING);
POPANDDUMP(FLOOR);
MARKMMNODES()
End;
! called by:
! LEAVE post-make
! EXITLOOP post-make
Global Routine F24 : Novalue =
Begin
Local
LABL : Ref ST,
LABLEVEL : Integer,
OLDINC : Integer;
If .swit_quick Then
Return;
If .NOTREE Then
Return;
! add a kill entry so that code may not move past this point
KILL(1,.SYM);
LABL = .SYM[gt_arg2];
! if this is the first leave then raise the levels up to the point
! of the labelled block. this makes all nodes after the leave
! look sorta like the else part of an if statement and thus follow
! the same rules for code motion as an else part.
If Not .LABL[st_lab_left] Then
Begin
LABL[st_lab_left] = TRUE;
OLDINC = .LABL[st_lab_inc];
LABLEVEL = .LABL[st_lab_level];
NOTELEAVE(CEILING,.LABLEVEL,.OLDINC);
NOTELEAVE(LVLCOPY,.LABLEVEL,.OLDINC);
LEVEL = .LEVEL+.OLDINC
End
End;
! called by:
! SFLABEL after the label node is created
Global Routine F25 : Novalue =
Begin
Local
S : Ref ST;
If .swit_quick Then
Return;
If .NOTREE Then
Return;
S = .SYM[gt_arg2];
! reduce the level increment
LEVELINC = .LEVELINC/2;
! if the label was really referenced then nothing created inside
! it is available as a CSE. Also, restore the level and ceiling
! to what they were before the leave.
If .S[st_lab_left] Then
Begin
PURGE();
REMOVELEAVEKILLS(.SYM);
CEILING[stk_data] = .CEILING[stk_data] - .LEVELINC;
LEVEL = LVLCOPY[stk_data] = .LEVEL - .LEVELINC
End
End;
! called by:
! SDO after the loop body is parsed
Global Routine F26 : Novalue =
Begin
GFDOWHILE()
End;
! called by:
! SCASE after the selector expression is parsed
Global Routine F27 : Novalue =
Begin
PushQueue(T_ALPHA);
MARKMMNODES();
PUSHANDBUMP(CEILING)
End;
! called by:
! SIF and SCASE when IF condition and CASE selector is
! found to be a constant.
Global Routine F28 : Novalue =
Begin
POPANDDUMP(CEILING)
End;
! called by:
! SCOMP after declarations and before first statement
Global Routine F29 : Novalue =
Begin
ABCOUNT = .ABCOUNT + 1
End;
End
Eludom