Google
 

Trailing-Edge - PDP-10 Archives - AP-D480B-SB_1978 - comsub.bli
There are 12 other files named comsub.bli in the archive. Click here to see a list.


!THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
!  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.

!COPYRIGHT (C) 1972,1977 BY DIGITAL EQUIPMENT CORPORATION
!AUTHOR: NORMA ABEL/HPW/DCE/SJW
MODULE COMSUB(RESERVE(0,1,2,3),SREG=#17,VREG=#15,FREG=#16,DREGS=4,GLOROUTINES)=
BEGIN

!	REQUIRES FIRST, TABLES, OPTMAC

GLOBAL BIND COMSV = 5^24 + 1^18 + 265;	!VERSION DATE: 22-SEP-77

%(
REVISION HISTORY

198	-----	-----	ADD INTERFACE TO I/O LIST OPTIMIZER
199	-----	-----	FIX MOVCNST TO SUBSUME
200	-----	-----	EXTEND XPUNGE TO COMPUTE PLACE FOR
			EXPRESSIONS ON AN I/O LIST
201	-----	-----	CORRECTED TYPOS FROM 199
202	-----	-----	REMOVED BAD DOT IN DOTOHASGN
203	-----	-----	REMOVED BAD COMPARE FROM UNRYMATCH
			MAKE PHI A PARAMETER TO CMNMAK
204	-----	-----	FIX MOVCNST TO DELETE HASH ENTRIES
			MOVE EHASHP TO BEGINNING OF MODULE
205	-----	-----	INTERFACE TO IOGELM TO WALK I/O LISTS AND
			DO GLOBAL SUBEXPRESSION ELIMINATION
206	-----	-----	MAKE DOTOHASGN AWARE OF I/O LISTS
			MAKE GLOBELIM AWARE OF I/O LISTS
207	-----	-----	WRONG PARAMETER IN CALL TO DOTOHASGN IN MATCHER
208	-----	-----	REARRANGE INTERACTION OF GLOBLDEPD,CHKHAIR AND
			MOVCNST
209	-----	-----	FIX SLINGHASH
210	-----	-----	FIX MOVCNST INPROVEMENT MADE IN 208
211	-----	-----	MORE TO COMPLETE 208 CORRECTLY
212	-----	-----	ADD AN ITERATION BETWEEN MOVCNST AND GLOBDEPD
213	-----	-----	ANOTHER FIX TO EDIT 208
214	-----	-----	ANOTHER ONE
216	-----	-----	REDEFINE CNSMOVFLG TO BE A BIT IN IMPLDO
217	-----	-----	FIX CHKDOMINANCE TO WORK CORRECTLY WITH
			CNSMOVFLG
215	-----	-----	AGAIN
218	-----	-----	MAKE THE ROUTINES FOR GLOBAL CMN ONLY INTO
			A SEPARATE MODULE
219	-----	-----	ADD THE VARIABLE OR EXPRESSION TO BE LINKED
			TO THE LIST OF PARAMETERS FOR CMNLNK
220	-----	-----	FIX CALL TO DOTOHASGN SO IT IS ONLY CALLED ONCE
221	-----	-----	DONT USE IDADDR FIELD SO DATA OPTS CAN BE DONE
222	-----	-----	MAKE NEXTUP LOOK AT SKEWED TREES FOR ARITHMETICS
223	-----	-----	CALL IOGELM FOR READ/WRITE/ENCODE/DECODE
224	-----	-----	ADD REREDID TO IOGELM
225	-----	-----	FIX ELIM TO TEST GCALLSLFLG
226	-----	-----	REEXAMINE DEFPTS IF OPTIMIZING IN HASHIT
227	-----	-----	ADD ARRAY REFERENCE PROCESSING
228	-----	-----	FIX TYPOS IN 227
229	-----	-----	MORE OF 228
230	-----	-----	CORRECT NEWCOPY TO RETURN POINTER TO NODE BUILT
231	-----	-----	ADD ROUTINE SCRUBARRAY AND MAKE CMNMAK GLOBAL
232	-----	-----	PUT ARRAY STUFF UNDER OPTIMIZER SWITCH.
			DELETE ARRAY SPECIFIEC ROUTINES.
233	----	-----	FIX ARRAY HASH KEY TO INCLUDE OFFSET
234	-----	-----	ADD ARRAYREF FUNCTION STUFF AND FIX
			NARY2 TO PUT ARRAY STUFF BACK INTO NODES
235	-----	-----	MAKE CHKHAIR AND MAKETR AWARE OF TREE SHAPE
236	-----	-----	CMNMAK IS BLOWING ARRAY REPLACEMENT
237	-----	-----	MAKE NEXTUP LOOK UP AS WELL AS DOWN FOR
			SKEWED EXPRESSIONS
238	-----	-----	MAKE MAKETRY TAKE BLKID FROM ENTRY OS ARRAYS
			WILL MATCH
239	-----	-----	IN MATCHER BUILD NARY EXPRESSION WITH ARG1
			AND ARG2 IN THE RIGHT PLACE
240	-----	-----	FIX MATCHER TO DEAL CORECTLY WITH
			NEXTUP NARY2 INTERFACE
241	-----	-----	SET NOHHASSLE BIT IN CMNMAK
242	-----	-----	FIX RANDOM P IN DELETE (V1 BUG TOO)
243	-----	-----	FIX LINKING OUT OF NODE TO BE DELETED
			IN ROUTINE DELETE
244	-----	-----	FIX HASHIT SO THAT A+B==-(A+B) IS
			FOUND AS A COMMON SUB
245	-----	-----	LIKE ARRAYREF NODES TOGETHER WHEN MATCHED
			AND RETRIEVE FROM LINKED LIST WHEN NEEDED.
			PARENT FIELD IS LINK
246	-----	-----	IN UNRYMATCHER IN LOCL CASE WE WILL MISS
			(MESS) BECAUSE T GET S CONFUSED
247	-----	-----	FIX NEGNOT FLAG BUG WITH SPECOPS
248	-----	-----	FIX MORE NEGNOT PROBLEMS WITH TYPCONV
			AND NEGNOTS
249	-----	-----	NEGNOTS ON SKEWED TREES MESSED UP
250	-----	-----	PUNT!
251	-----	-----	CMNMAK SHOULD CALL PUTBACKARRAY TO PUT THE
			ARRAREF BACK. ALL THE RIGHT LOGIC IS THERE.
252	-----	-----	THE RIGHT LOGIC MAY THERE IN EDIT 251 BUT
			ITS FOR THE WRONG (POTENTIALLY) EXPRESSION.
253	-----	-----	LOK1SUBS CASE STATEMENT IS
			MISSING ARRAYREF
254	-----	-----	UNARY MATCHER IS CALLING NEXTUP
			IMPROPERLY FOR A LOCAL SPECIAL CASE
255	371	18471	FIX CSE FOR STRAIGHT NON-SKEWED CASE
256V	VER5	-----	X OP .R -> .R OP X
			FIND CSE'S CONTAINING ARRAYREFS IF OPTIMIZING
			RETURN OMOVDCNS FOR LOK1/2SUBS TO GLOBDEPD
			DON'T ELIM I/O STATEMENTS ON 2ND GLOBELIM
256	405	18967	FIX IOLISTS WITH STAR1 AND STAR2 SHAPES
257	427	18771	FIX IOLISTS WITH COMMON SUBS WHICH ARE
			SHAPES GREATER THAN SKEW
258	442	19233	FIX DELETE SO IT DOESN'T LOSE SOME HASH
			ELEMENTS OTHER THAN ONE BEING DELETED
259	450	QA784	DON'T NEXTUP AN ARRAYREF INSIDE AN IOLIST
260	456	QA784	PASS ENTIRE HASH ENTRY TO GLOBMOV IN CMNMAK
			  FOR FINDPA FOR FINDTHESPOT

	BEGIN VERSION 5A, 7-NOV-76

261	520	21271	RELATIONAL COMMON SUBS CANNOT HAVE NEG FLAGS SET
262	524	QA876	PUT BACK ARRAY REF IN STPRECLUDE AFTER A MATCH
			  SO CAN NEXTUP THE EXPR CONTAINING THE ARRAY
			  REF AND NOT RUN INTO A HASH TABLE ENTRY
			CALL STPRECLUDE BEFORE CMNMAK CHANGES NEG
			  FLAGS IN MATCHER
263	566	22701	SKEWED COMSUBS INVOLVING NOTFLAGS ARE NOT
			HASHED UNIQUELY - USE THE CORRECT FLAGS!
264	602	22700	FIX SKEWED EXPRESSIONS IN IOLISTS WITH
			CORRECT DEFINITION POINT CALCULATION
265	620	23720	D.P. ARRAY REF IN IO LIST CAUSES PROBLEMS

)%


SWITCHES NOLIST;
REQUIRE FIRST.BLI;
REQUIRE TABLES.BLI;
REQUIRE OPTMAC.BLI;
SWITCHES LIST;
SWITCHES  NOSPEC;


!***************************************************************
!	COMMON SUB EXPRESSION ELIMINATION MODULE.
!	LOCAL AND GLOBAL BOTH INCLUDED TOGETHER WITH
!	MOTION OF CONSTANT EXPRESSIONS OUT OF LOOPS IN THE GLOBAL CASE
!	THE LOCAL CONTROL ROUTINE IS LOCELIM.
!	THE GLOBAL CONTROL ROUTINE IS GLOBELIM.
!******************************************************************
!	NOTE:
!		THERE ARE TWO WAYS USED TO DISTINGUISH BETWEEN THE
!		LOCAL AND GLOBAL CASES.
!			1. BACKST=0; GLOBAL CASE
!			   BACKST#0; LOCAL CASE
!			2. FLGRG<OPTIMIZE>=1; GLOBAL CASE
!			   FLGRG<OPTIMIZE>=0; LOCAL CASE
!		TESTS ON THESE ARE MADE INTERCHANGABLY. THE
!		TWO WAYS ARE PRESENT FOR HISTORIC REASONS.

EXTERNAL CORMAN,LENTRY,QQ,TOP,BOTTOM,TPREV;
MAP PEXPRNODE LENTRY;
EXTERNAL GETOPTEMP;
FORWARD TBLSRCH,DELETE,MAKETRY,XPUNGE;
EXTERNAL PHI;
OWN P,PAE,PB,PA,PC,PO,T;

EXTERNAL EHASHP;
EXTERNAL LOCLNK;
OWN P1,P2;
MAP PEXPRNODE P1:P2;
MAP PEXPRNODE P:PHI:PAE:PA:PB:PC:QQ:PO;
EXTERNAL MAKEPR;

MAP PHAZ2 TPREV;
EXTERNAL GLOBMOV,GLOBDEPD,CHKINIT,DOTOHASGN;
FORWARD HASHIT,LOCLMOV,LOCLDEPD;
OWN STHASCMN;		!USED BY LOCAL COMMONSUB EXPRESSIONS TO
			!TO SAVE A SCAN OF EHASH IF THE STATEMENT
			!WAS NOT EVEN ONE THAT POTENTAILLY HAD A
			!COMMON SUB-EXPRESSION (LIKE END).


EXTERNAL NAN;
OWN	TS,		!TEMP USED THROUGH OUT
	VARHOLDER,	!USED IN SPECIAL LOCAL CASES SEE UNRYMATCHER
	NEDCOR;		!FLAG SET BY TBLSRCH TO INDICATE IF THE
			!HASH TABLE HAS A FREE REUSABLE SPACE OR
			!CORE IS NEEDED FOR THE ENTRY. 1=LATTER.

EXTERNAL ARGCONE;
EXTERNAL NEWCOPY,A2ARREF,A1ARREF;


GLOBAL ROUTINE CMNMAK(PAE,NAN,PHI)=
BEGIN
	!CREATE A COMMON SUB-EXPRESSION NODE POINTING TO
	!THE EXPRESSION
	!A COMMON SUB-EXPRESSION NODE HAS
	!	OPRCLS - CMNSUB
	!	OPERSP - NULL
	!	A SINGLE ARGUMENT (ARG2PTR) POINTING TO
	!	THE EXPRESSION (OR SINGLE VARIABLE)

	MAP PEXPRNODE PAE;
	MAP PHAZ2 PHI;
	EXTERNAL PUTBACKARRAY;

	!THIS ROUTINE IS CALLED IN BOTH THE GLOBAL AND LOCAL CASES
	!THIS IS THE POINT AT WHICH THEY DIVERGE.
	!A POINTER TO A CMNSUB *EXPRESSION NODE* IS RETURNED IN THE LOCAL
	!CASE. A POINTER TO A TEMP IN THE GLOBAL CASE.
	!IN BOTH CASES PHI[TEMPER] IS SET CORRECTLY AND RETURNED.


	!IF DOING AN ARRAY REFERENCE PICK UP THE TO THE HASH TABEL
	!AND THE ARRAYREFERENCE ITSELF. THIS ADJUSTMENT IS MADE TO
	!THE EXPRESSION BEFORE ANY OTHER PROCESSING HERE.


	!IF THE FLAG NAN (NEEDS A NEGATE) IS SET, COMPLEMENT/RESET
	!THE NEGFLAGS IN THE EXPRESSION BEFORE IT BECOMES THE
	!COMMON SUBEXPRSSSION.

	IF .NAN THEN
	BEGIN
		!FOR AN ADD COMPLEMENT THE FLAGS
		IF .PAE[OPR1] EQL ADDOPF THEN
		BEGIN
			PAE[A1NEGFLG]_NOT .PAE[A1NEGFLG];
			PAE[A2NEGFLG]_NOT .PAE[A2NEGFLG];
		END ELSE
		IF (.PAE[OPRCLS] EQL ARITHMETIC) OR
		  (.PAE[OPRCLS] EQL SPECOP) OR
		  (.PAE[OPRCLS] EQL TYPECNV) OR
		  (.PAE[OPRCLS] EQL NEGNOT) THEN
		!MULTIPLE, DIVIDE,EXPONENTIATE
		BEGIN
			PAE[A1NEGFLG]_0;
			PAE[A2NEGFLG]_0;
		END;
	END;

	IF NOT .FLGREG<OPTIMIZE> THEN
	BEGIN
		NAME<LEFT>_4;
		P_CORMAN();
		P[OPRCLS]_CMNSUB;
		P[ARG2PTR]_.PAE;
		IF .PAE[VALTYPE] NEQ CONTROL THEN
			P[VALTYPE]_.PAE[VALTYPE]
		ELSE
			P[VALTYPE]_LOGICAL;
		IF .PAE[OPRCLS] EQL DATAOPR THEN
		P[A2VALFLG]_1;
		PHI[TEMPER]_.P;
		!CALL ROUTINE THAT WILL ADD EXPRESSION TO LINKED LIST
		LOCLMOV(.P);


	END ELSE		!GLOBAL CASE
	BEGIN
	IF .ARREFCMNSBFLG THEN
	BEGIN
		NOHHASSLE_1;

		!TAKE CARE OF POTENTIAL ARRAY REFERENCES
		!WE KNOW THE SHAPE IF THE FIRST EXPRESSION BUT
		!THIS DOES NOT RELATE (NECESSARILY) TO PAE
		!SO WE WILL EXPLICITLY EXAMINE PAE TO PUT THE
		!ARAYREF BACK.

		IF (P_.PAE[ARG1PTR]) NEQ 0 THEN
			!IS IT THE ARRAYREF HASH ENTRY
			!COINCIDENCE BETWEEN HOP AND OPRCLS MAKES
			!THIS TEST POSSIBLE
			IF .P[OPRCLS] EQL ARRAYREF THEN
				PAE[ARG1PTR]_NEWCOPY(.P,.PAE);

		!IT COULD ALSO BE THAT PAE IS A FUNCTION REF
		IF .PAE[OPRCLS] EQL FNCALL THEN
			PUTBACKARRAY(.PHI,STGHT)
		ELSE
		BEGIN
			IF (P_.PAE[ARG2PTR]) NEQ 0 THEN
				IF .P[OPRCLS] EQL ARRAYREF THEN
					PAE[ARG2PTR]_NEWCOPY(.P,.PAE);
		END;


	END;
		P_PHI[TEMPER]_GETOPTEMP(IF .PAE[VALTYPE] EQL CONTROL THEN
			 LOGICAL ELSE .PAE[VALTYPE]);
		P[IDOPTIM]_.PAE;
		!CALL ROUTINE THAT CREATS AND LINKS IN ASSIGNMENT STATEMENT
!**[456] CMNMAK @3780 SJW 22-SEP-76
![456] PASS GLOBMOV ENTIRE HASH ENTRY SO CAN DO FINDPA FOR FINDTHESPOT
%[456]%		GLOBMOV (.PAE, .PHI, .P);
	END;

	.PHI[TEMPER]
END;


EXTERNAL FINDTHESPOT;



ROUTINE STPRECLUDE(CNODE)=
BEGIN
	!PART OF THE COUNT HASSLE.
	!IF A COMMON - SUB EXPRESSION HAS JUST BEEN FOUND
	!THAT IS PART OF AN NARY STRUCTURE IT MAY HAVE TO BE DELETED.
	!FOR THE GLOABL CASE IT MUST BE DELETED TO PREVENT IT FROM
	!APPEARING TO BE A CONSTANT COMPUTATION. IN THE LOCAL CASE
	!IT MAY OCCUR WHEN AN NARY ENTRY HAS BEEN MADE WHILE WALKING ONE
	!TREE AND IT IS DISCOVERED THAT (WHILE WALKING ANOTHER TREE)
	!THE WHOLE THING COLLAPSES UPWARD AS A COMMON-SUB.

	EXTERNAL SKERR;
	MAP BASE QQ:TS:CNODE;

	QQ_.CNODE[PARENT];
	!CHECK FOR ERROR
	IF .QQ EQL 0 THEN SKERR();

	!SEE IF IT NARY

!**[524] STPRECLUDE @3815 SJW 2-DEC-76
%[524]%	IF .QQ [OPR1] NEQ .CNODE [OPR1]
%[524]%	  THEN RETURN;
%[524]%	IF .QQ [A2VALFLG]
%[524]%	  THEN BEGIN
		!NOW ITS NARY CHECK FOR B OP B OP B
		IF (.CNODE[ARG1PTR] EQL .CNODE[ARG2PTR]) AND
		   (.CNODE[ARG2PTR] EQL .QQ[ARG2PTR]) THEN
			!GET THE #$$$() OUT
			RETURN;
		HASHIT(.QQ,SKEW);
		TS_TBLSRCH();
		!IF ITS THIS ONE (JUDGING BY SHAPE AND NOT
		!A COMMON SUB IN ITS OWN RIGHT ALREADY FOR OTHER
		!REASONS THEN DELETE IT
		IF .FLAG THEN
			IF .TS[TEMPER] EQL 0 AND .TS[NBRCH] THEN
				DELETE(.TS,1);
	END;
!**[524] STPRECLUDE @3830 SJW 2-DEC-76
![524] IF /OPT, MUST CHECK IF EXPR IS A OP B OP ARRAYREF.  IF IT IS,
![524] MUST DROP USECNT OF HASH ENTRY FOR ARRAYREF BY 1 SO MAYBE THE
![524] ARRAYREF WILL BE PUT BACK IN PLACE OF THE HASH TABLE ENTRY

%[524]%	IF NOT .FLGREG<OPTIMIZE>
%[524]%	  THEN RETURN;
%[524]%	HASHIT (.QQ, SKEW);
%[524]%	TS _ TBLSRCH ();
%[524]%	IF .FLAG  THEN IF
%[524]%	   .TS [TEMPER] EQL 0  THEN IF
%[524]%	   .TS [NBRCH]
%[524]%	  THEN DELETE (.TS, 1);

END;
ROUTINE NARY2(CNODE)=
	!ROUTINE CALLED FROM MATCHER WHEN A MATCH ON A SKEWED
	!TREE HAS BEEN MADE. A SKEWED NODE IS OP A OP B. WE
	!NEED TO DELETE FROM THE HASH TABLE USED OF
	!ANYTHING OP A AND B OP ANYTHING, AT THE SAME TIME
	!BEING CAREUL NOT TO *MESS-UP* B OP B OP B OP B OP B.
BEGIN
	OWN BSAMEFLG;
	MAP BASE TS;
	MAP PEXPRNODE CNODE;

	ROUTINE BOPBOPB(SHAPE)=
	BEGIN
		!LOCAL ROUTINE TO SAVE SOME CODE SPACE
		!HASHES EXPRESSION OF SHAPE SHAPE, LOOKS
		!IT UP IN THE TABLE AND DELETES IT USE COUNT BY 1
		!SET UP HASH KEY
		HASHIT(.QQ,.SHAPE);
		!DO TABLE LOOKUP
		TS_TBLSRCH();
		!IF ENTRY WAS IN THE TABLE AND IT MATCHES
		!IN SHAPE WITH THE ONE NOW CONSIDERED, THEN
		!DECREASE ITS USE COUNT BY 1. SEE DOCUMENTATION
		!FOR A DETAILED DESCRIPTION OF WHY, WHO, HOW.
		IF .FLAG THEN
			IF .TS[TEMPER] EQL 0 THEN
			IF (.SHAPE EQL SKEW AND .TS[NBRCH]) OR
			   (.SHAPE EQL STGHT AND NOT .TS[NBRCH]) THEN
				 DELETE(.TS,1);
	END;

	QQ_.CNODE[ARG1PTR];
	!FIRST DECIDE IF THIS IS A OP A. IT IS A PROPBLEM, IF IT IS
	BSAMEFLG_0;
	IF .QQ[ARG2PTR] EQL .CNODE[ARG2PTR] THEN BSAMEFLG_1;
	IF .QQ[A1VALFLG] AND .QQ[A2VALFLG] THEN
	BEGIN
		IF .BSAMEFLG AND .QQ[ARG1PTR] EQL .CNODE[ARG2PTR] THEN
		!WE HAVE B OP B OP B
		ELSE
			BOPBOPB(STGHT);
	END
	ELSE
	BEGIN
		!LOOK DOWN ONE MORE
		QQ_.QQ[ARG1PTR];
		IF .BSAMEFLG AND .QQ[ARG2PTR] EQL .CNODE[ARG2PTR] THEN
		!WE HAVE B OP B OP B
		ELSE
		BEGIN
			!FOR THE HASH, WHICH DEPENDS ON QQ
			!IN THIS CASE, MOVE QQ BACK UP
			QQ_.CNODE[ARG1PTR];

			!LOOKING DOWN MAY CAUSE AN ARRAY HASH ENTRY
			!TO APPEAR IF OPTIMIZING. WE WILL HAVE TO
			!SPECIAL CASE IT HERE

			IF .FLGREG<OPTIMIZE> THEN
			BEGIN
				HASHIT(.QQ,STGHT);
				!TRY HASHING IT STRAIGHT AND SEE IF IT
				!IS THERE WITH THE ARRAY BITS SET

				TS_TBLSRCH();

				IF .FLAG THEN
				BEGIN
					IF .TS[A1ARY] OR .TS[A2ARY] THEN
						DELETE(.TS,1);
				END;
			END;

			BOPBOPB(SKEW);
		END;
	END;
	QQ_.CNODE[PARENT];
	IF .QQ[OPR1] EQL .CNODE[OPR1]
	THEN
	BEGIN
		IF .BSAMEFLG AND .QQ[ARG2PTR] EQL .CNODE[ARG2PTR] THEN
		!ONCE AGAIN B OP B OP B
		ELSE
			BOPBOPB(SKEW);
	END;
END;

!
!********************************************
!

%*********************************
MACROS TO CHECK PROPER VALFLAGS AND CALL REA FOR THE NEXT LEVEL UP
OF AN EXPRESSION THAT HAS JUST BEEN MATCHED.
************************************%

MACRO ARGSBOTH(NOD)=
		IF .NOD[A1VALFLG] AND .NOD[A2VALFLG] THEN XPUNGE(.NOD,STGHT)$;
MACRO ARGS1(NOD)=
		IF .NOD[A1VALFLG] THEN XPUNGE(.NOD,UNARY)$;

MACRO ARGS2(NOD)=
		IF .NOD[A2VALFLG] THEN XPUNGE(.NOD,UNARY)$;


FORWARD CHKHAIR,REA,CMNLNK;


ROUTINE NEXTUP(EXPR)=
BEGIN
	!CASE STATEMENT CONTROL ON LOOKING AT THE NEXT EXPRESSION
	!UP AFTER A MATCH.

	MAP BASE EXPR;
	EXTERNAL BASE QQ;

%[V5]%	EXTERNAL  A1ARREF, A2ARREF;

	IF .EXPR EQL 0 THEN RETURN;
	CASE .EXPR[OPRCLS] OF SET

		ARGSBOTH(EXPR);		!BOOLEAN
		RETURN;			!DATAOPR - ILLEGAL
		!RELATIONALS ARE SPECIAL. AT THIS POINT
		!WE ARE ONLY INTERESTED IN THE RELATIONAL ITSELF
		!AND NOT THE LOCAL SPECIAL SINGLE VARIABLE CASE AT ALL
		!REA WOULD INCLUDE THIS SPECAIL CASE.
		!WE WILL CALL XPUNGE DIRECTLY TO PREVENT THIS
		IF .EXPR[A1VALFLG] AND .EXPR[A2VALFLG] THEN XPUNGE(.EXPR,STGHT);
		!FNCALL
		IF ARGCONE(.EXPR) THEN XPUNGE(.EXPR,UNARY);
		BEGIN	!ARITHMETIC
			!GET THE OBVIOUS STRAIGHT CASE
			ARGSBOTH(EXPR)
%[V5]%			ELSE BEGIN		! CHECK ARRAY REFS
%[V5]%			  QQ _ .EXPR [ARG1PTR];
%[V5]%			  IF .FLGREG<OPTIMIZE> AND .QQ [OPRCLS] EQL ARRAYREF
%[V5]%			    THEN A1ARREF (.EXPR)
%[V5]%			    ELSE BEGIN
%[V5]%			      QQ _ .EXPR [ARG2PTR];
%[V5]%			      IF .FLGREG<OPTIMIZE> AND .QQ [OPRCLS] EQL ARRAYREF
%[V5]%				THEN A2ARREF (.EXPR)
			ELSE
			BEGIN
				!TRY FOR SKEWED TREES TOO
				QQ_.EXPR[ARG1PTR];
				!CHECK SKEW PROPERTIES
				IF (.QQ[OPR1] EQL .EXPR[OPR1])
				   AND
				   (.QQ[OPR1] NEQ DIVOPF)
				   AND
				   (NOT .QQ[PARENFLG])
				   AND
				   .QQ[A2VALFLG]
				THEN
					XPUNGE(.EXPR,SKEW);

				!LOOK UP
				IF (QQ_.EXPR[PARENT]) NEQ 0 THEN
				BEGIN
					IF (.QQ[OPR1] EQL .EXPR[OPR1])
					   AND
					   (.QQ[OPR1] NEQ DIVOPF)
					   AND
					   (NOT .EXPR[PARENFLG])
					   AND
					   .QQ[A2VALFLG] AND .EXPR[A2VALFLG]
					THEN
						XPUNGE(.QQ,SKEW);
				END;
%[V5]%			END;
			    END;
			END;
		END;
		ARGS2(EXPR);		!TYPECNV

%[V5]%		BEGIN			!ARRAYREF
%[V5]%		  LOCAL BASE  MOM;
%[V5]%
%[V5]%		  IF NOT .FLGREG<OPTIMIZE>
%[V5]%		    THEN RETURN;
%[V5]%		  MOM _ .EXPR [PARENT];
%[V5]%		  IF .MOM [OPRCLS] EQL STATEMENT
%[V5]%		    THEN RETURN;
!**[450] NEXTUP @4009 SJW 17-SEP-76
![450] CAN'T NEXTUP ARRAYREF IF INSIDE IOLIST
%[450]%		  IF .MOM [OPRCLS] EQL IOLSCLS
%[450]%		    THEN RETURN;
%[V5]%					! FIND TREE SHAPE & CALL XPUNGE
%[V5]%		  IF .MOM [ARG1PTR] EQL .EXPR
%[V5]%		    THEN A1ARREF (.MOM)
%[V5]%		    ELSE A2ARREF (.MOM);
%[V5]%		END;

		RETURN;			!CMNSUB - ILLEGAL
		ARGS2(EXPR);		!NEGNOT
		ARGS1(EXPR);		!SPECOP
		RETURN;			!FIELDREF
		RETURN;			!STORECLS
		RETURN;			!REGCONTENTS
		RETURN;			!LABOP
		RETURN;			!STATEMENT
		RETURN;			!IOLSCLS
		BEGIN			!INLINFN
			IF .EXPR[ARG2PTR] NEQ 0 THEN
			BEGIN
				ARGSBOTH(EXPR)
			END ELSE
				ARGS1(EXPR);
		END
	TES;
END;
EXTERNAL REPLACARG;
ROUTINE UNRYMATCH(CNODE,NAN,PHI)=
BEGIN
!FIXES UP (PERFORMS MATCHER FUNCTIONS ) FOR A UNARY SHAPE
!(TYPECNV,ARRAYREF SPECIAL CASE, LIBRARY FUNCTION).
!PHI IS POINTER TO HASHED ENTRY (INDEX).
!NAN SHOULD BE SET ONLY ON NOT NODES, TYPECNV NODES OR FUNCTION CALLS

	MAP PEXPRNODE CNODE:PHI;

	!GET OUT IF ITS AN ARRAYREF
	IF .CNODE[OPRCLS] EQL ARRAYREF THEN
	BEGIN
		!BETTER ONLY BE HERE WHEN GLOBALLY OPTIMIZING
		IF .FLGREG<OPTIMIZE> THEN
		BEGIN
			!MAKE LINKED LIST OFF OF LKER FIELD OF PHI
			!USING PARENT POINTERS OF ARRAYREF NODES
			CNODE[PARENT]_.PHI[LKER];
			PHI[LKER]_.CNODE;
		END;
		RETURN;
	END;


	!IN THE GLOBAL CASE CHECK THE FIRST ENTRY TO SEE IF IT IS
	!AN ASSIGNMENT TO A OPTIMIZER .O TEMP.
	IF .FLGREG<OPTIMIZE> THEN
		IF .CNODE[OPRCLS] NEQ DATAOPR
		   AND .PHI[USECNT] EQL 1 THEN
		BEGIN
			DOTOHASGN(.PHI[LKER],.PHI);
			!IF SUBSUMPTION HAPPENED THEN
			!DO ONLY THE SECOND HALF A QUIT
			IF .PHI[TEMPER] NEQ 0 THEN
			BEGIN
				PHI[USECNT]_.PHI[USECNT]+1;
				CMNLNK(.PHI[TEMPER],.CNODE,UNARY,.NAN,.PHI);
				RETURN;
			END;
		END;

	IF .PHI[USECNT] EQL 1 THEN
	BEGIN
		PHI[USECNT]_2;
		!MAKE CMNSUB NODE AND FIX UP OLD ENTRY
		!WE MUST SET PC BEFORE THE CALL TO CMNMAK
		!IN ORDER TO CORRECTLY HANDLE THE SPECIAL LOCAL
		!COMMON SUB-EXPRESSION CASE OF A SINGLE VARIABLE
		!SUBSCRIPT OR UNDER A RELATIONAL. IN THIS
		!CASE PHI[TEMPER] HOLDS THE POINTER TO THE
		!EXPRESSION FOR RELINKING. CMNMAK WILL CHANGE 
		!PHI[TEMPER]

		IF .CNODE[OPRCLS] EQL DATAOPR THEN
			PC_.PHI[TEMPER] ELSE PC_.PHI[LKER];

		!FIRST RESET ANY NEG FLAGS ON THE NODE WE ARE
		!ABOUT TO MAKE A COMMON SUB-EXPRESSION
		!QQ IS USED AS A TEMP
		QQ_.PHI[LKER];
		IF .QQ[OPRCLS] NEQ DATAOPR THEN
		BEGIN
			QQ[A1NEGFLG]_0;
			QQ[A2NEGFLG]_0;
		END;

		T_CMNMAK(.PHI[LKER],0,.PHI);
		QQ_CMNLNK(.T,.PC,UNARY,.PHI[NEDSANEG],.PHI);
		!QQ CONTAINS THE PARENT POINTER ON RETURN.
		!BUT BE CAREFUL**********.
		!CHECK FOR THE SPECIAL LOCAL CASE OF
		!RELATIONAL OR ARRAYREF, CUZ QQ WILL POINT TO
		!THE RELATIONAL OR ARRAYREF.
		IF .CNODE[OPRCLS] EQL DATAOPR THEN
		ELSE
		NEXTUP(.QQ);
	END ELSE
		PHI[USECNT]_.PHI[USECNT]+1;

	T_.PHI[TEMPER];

	!NOW FIX UP CURRENT REFERENCE (IN ALL CASES)
	!NOTE THE SPECIAL TEST FOR THE SAME REASON AS DESCRIBED ABOVE
	!. VARHOLDER IS THE MODULE OWN THAT POINTS TO THE EXPRESSION
	!IN THIS CASE

	CMNLNK(.T,IF .CNODE[OPRCLS] EQL DATAOPR THEN .VARHOLDER ELSE .CNODE,UNARY,.NAN,.PHI);
END;
ROUTINE CHK4OPS(CNODE)=
BEGIN
	!IF WE HAVE A OP A OP A OP A
	!WHEN WE ARE MATCHING A OP A WITH A OP A WE
	!WOULD UNLESS OTHERWISE PREVENTED ENTER INTO THE
	!HASH TABLE CMN(A OP A) OP A. IF THERE IS ANOTHER
	!A OP A OP A OP A IN THE WORLD THIS WILL
	!LEAD TO A FALSE MATCH AND INCORRECT CODE.
	!THE PURPOSE OF THIS ROUTINE IS TO PREVENT THAT
	!TROUBLE MAKING ENTRY. 0 IS RETURNED IF THE ENTRY IS OK
	!1 IF NOT.

	MAP PEXPRNODE CNODE:TS;

	IF NOT .CNODE[A1VALFLG] THEN
	BEGIN
		!THE TREE MUST BE SKEWED.
		TS_.CNODE[ARG1PTR];
		!CHECK FOR A OP A
		IF .TS[ARG2PTR] EQL .CNODE[ARG2PTR] THEN
		BEGIN
			!IF ARG1 IS THE NODE WERE SUBSTITUTING THEN
			!THIS IS THE BAD CASE.
			IF .TS[ARG1PTR] EQL .T THEN
			RETURN(1);
		END;
	END;
END;
ROUTINE MATCHER(CNODE,SHAPE,NAN,PHI)=
!CALLED ON A HIT IN THE HASH TABLE.
!PHI POINTS TO THE MATCHING ENTRY.
!CNODE IS EXPRESSION NODE.
!SHAPE IS UNARY,STGHT(STRAIGHT), OR SKEW(SKEWED) OF CURRENT EXPRESSION
!SEE ROUTINE HASHIT FOR PICRURES OF THE TREE SHAPES
!NAN IS NEEDS A NEGATIVE(NEGATION)
BEGIN
	EXTERNAL OLDHEAD;
	MAP PHAZ2 CNODE:PHI:T;
	!GO TO SPECIAL ROUTINE IF SHAPE IS UNARY
	IF .SHAPE EQL UNARY THEN
	BEGIN
		UNRYMATCH(.CNODE,.NAN,.PHI);
		RETURN;
	END;


	!CHECK FOR ASSIGNMENT TO .O VARIABLE
	IF .FLGREG<OPTIMIZE> THEN
		IF (.SHAPE EQL STGHT)
		   AND (.PHI[USECNT] EQL 1)
		   AND NOT .PHI[NBRCH]
		   THEN DOTOHASGN(.PHI[LKER],.PHI);

	IF .PHI[USECNT] EQL 1 AND .PHI[TEMPER] EQL 0  THEN
	BEGIN
		PHI[USECNT]_2;
		IF .SHAPE EQL SKEW THEN		!SKEWED TREE
		BEGIN
			!ALSO CATCH B*B*B*B, TEST AFTER NARY2 WILL
			!STOP ALL COMMON SUBS OR MERELY CORRECT THE
			!SITUATION FOR B*B*B, HOPEFULLY, THIS WILL
			!PERMIT B*B*B*B TO BECOME (T=B*B,T*T).
			IF .CNODE[ARG1PTR] EQL .PHI[LKER] THEN
			BEGIN
				PHI[USECNT]_1;
				!GET OUT
				RETURN;
			END;
			NARY2(.CNODE);		!PRECLUDE TRIPLES ELIMINATED BY MATCH
			IF .PHI[NBRCH] THEN	!ENTRY IN HASH TABLE IS ALSO SKEWED
			BEGIN
				QQ_.CNODE[ARG1PTR];
				PB_MAKEPR(.CNODE[OPRCLS],	!MAKE A STRAIGHT ONE
				.CNODE[OPERSP],
				.CNODE[VALTYPE],
				.QQ[ARG2PTR],
				.CNODE[ARG2PTR]);
				PB[DEFPT1]_.QQ[DEFPT2];
				PB[DEFPT2]_.CNODE[DEFPT2];
				PB[A1FLGS]_.QQ[A2FLGS];
				PB[A2FLGS]_.CNODE[A2FLGS];
				NARY2(.PHI[LKER]);	!ELIMINATE TRIPLES PRECLUDED BY MATCH
				T_CMNMAK(.PB,.NAN,.PHI);	!MAKE A CMN SUB
				PC_.PHI[LKER];
				PB[PARENT]_.PC[PARENT];
				PHI[LKER]_.PB;
			END ELSE
			BEGIN
!**[524] MATCHER @4241 SJW 17-DEC-76
![524] CALL STPRECLUDE BEFORE CMNMAK CHANGES NEG FLAGS SO HASH IN
![524]   STPRECLUDE CAN FIND THE SKEW PIECE OF TREE
%[524]%				!PRECLUDE IF NECESSARY
%[524]%				STPRECLUDE(.PHI[LKER]);

				T_CMNMAK(.PHI[LKER],.PHI[NEDSANEG],.PHI);	!MAKE A CMNSUB
				PC_.PHI[LKER];
			END;			!FIX UP TREE
		END ELSE
		BEGIN		!SHAPE IS STRAIGHT
			!MAKE CMNSUB NODE
			!**;[371], MATCHER @4161, DCE, 14-APR-76
			!**;[371], TAKE INTO CONSIDERATION THE NON-SKEWED CASE
	%[371]%		IF .PHI[NBRCH] THEN
	%[371]%			BEGIN
	%[371]%				T_CMNMAK(.CNODE,.NAN,.PHI);
					![371] IF FIRST NODE WAS SKEWED, MAKE THE ONE
					![371] WE *KEEP* STRAIGHT. ALSO PRECLUDE OTHERS
					![371] PRECLUDED BY THIS MATCH.
	%[371]%				NARY2(.PHI[LKER])
	%[371]%			END
	%[371]%			ELSE
	%[371]%			BEGIN
!**[524] MATCHER @4262 SJW 17-DEC-76
![524] CALL STPRECLUDE BEFORE CMNMAK CHANGES THE NEG FLAGS
%[524]%					![371] FIRST NODE WAS STRAIGHT
%[524]%					![371] BUT STILL HAVE THE COUNT HASSLE
%[524]%	%[371]%				STPRECLUDE(.PHI[LKER]);
	%[371]%				T_CMNMAK(.PHI[LKER],
	%[371]%					.PHI[NEDSANEG],.PHI);
	%[371]%			END;
	%[371]%		PC_.PHI[LKER];
	%[371]%		PHI[LKER]_.CNODE
		END;
		!FIX UP  EXPRESSION PTRS
		!PC IS POINTER TO THE EXPRESSION
		QQ_CMNLNK(.T,.PC,IF .PHI[NBRCH] THEN SKEW ELSE STGHT,.PHI[NEDSANEG],.PHI);
		!QQ POINTS TO *PARENT* ON RETURN FROM CMNLNK
		IF NOT CHK4OPS(.CNODE) THEN
		NEXTUP(.QQ);
	END ELSE
		PHI[USECNT]_.PHI[USECNT]+1;	!USECNT ATR 1

	!IF THIS IS A SKEWED TREE DELETE
	!HASH ENTRIES PRECLUDED BY THIS MATCH
	IF .SHAPE EQL SKEW THEN
		NARY2(.CNODE)
	ELSE
		STPRECLUDE(.CNODE);

	T_.PHI[TEMPER];			!POINT TO TEMP FOR SUBSTITUTION

	!LINK UP THE COMMON SUB EXPRESSION (CURRENT ONE)
	CMNLNK(.T,.CNODE,.SHAPE,.NAN,.PHI);
END;							!MATCHER
!*********************************
ROUTINE CMNLNK(T,CNODE,SHAPE,NAN,PHI)=
BEGIN
	EXTERNAL LOKCALST,BACKST,ARGCONE;
!LINK UP THE COMMON SUB-EXPRESSION IN ITS PLACE

	MAP PHAZ2 QQ;
	MAP BASE CNODE:PHI:T;
	EXTERNAL MAKPR1,SETNEG,SAVSPACE;
	EXTERNAL CHOSEN,GLOBREG,ITMCT,SPECCASE;
	OWN OLDT,NEGT;
	LABEL ADJCTL;

	T[EXPRUSE]_.PHI[USECNT];

	!INITIALIZE FLAG TO 0
	FLAG_0;

	IF .SHAPE EQL SKEW THEN
	BEGIN
		CNODE[A2NEGFLG]_0;
		IF .NAN THEN
		BEGIN
			CNODE[ARG2PTR]_MAKPR1(.CNODE,NEGNOT,NEGOP,.CNODE[VALTYPE],0,.T);
			CNODE[A2VALFLG]_0;
		END
		ELSE
		BEGIN
			CNODE[ARG2PTR]_.T;
			CNODE[A2VALFLG]_1;
		END;
		!THE TREE LOOKSE LIKE THIS
		!	  *(CNODE)
		!    *        *
		!*(QQ)	  	*(JUST BECOME T)

		!	  *(QQ)
		!    *        *
		! *(WILL BECOME QQ WE CARE ABOUT)
		!			*(WILL GO AWAY)

		QQ_.CNODE[ARG1PTR];
		!SAVE DEFINITION POINT. IF QQ[ARG1PTR] IS NOT
		!AN EXPRESSION THEN THIS BECOMES THE DEFINITION POINT
		!OF THE DATAOPR, ETC.
		TS_.QQ[DEFPT1];
		!THERE IS YET ANOTHER HASSLE.****NEG/NOT FLGS
		!WE MUST MOVE THEM UP IF POSSIBLE. THE CONDITIONS ARE
		!IF NEG IS SET AND CNODE DOESNT HAVE NOT SET, COMPLEMENT
		!NEG IN PARENT ELSE BUILD NEG NODE AND INSERT. THE
		!SAME IS TRUE FOR NOTS. SET REFERS TO THE ARG1FLGS OF
		!THE TOP ONE OF THE QQ  NODES IN THE ABOVE DIAGRAM.

		IF .QQ[A1NEGFLG] THEN
		BEGIN
			IF NOT .CNODE[A1NOTFLG] THEN
			BEGIN
				CNODE[A1NEGFLG]_NOT .CNODE[A1NEGFLG];
				QQ_.QQ[ARG1PTR];
			END ELSE
				QQ_MAKPR1(.CNODE,NEGNOT,NEGOP,.QQ[VALTYPE],0,.QQ);
		END ELSE
		IF .QQ[A1NOTFLG] THEN
		BEGIN
			IF NOT .CNODE[A1NEGFLG] THEN
			BEGIN
				CNODE[A1NOTFLG]_NOT .CNODE[A1NOTFLG];
				QQ_.QQ[ARG1PTR];
			END ELSE
				QQ_MAKPR1(.CNODE,NEGNOT,NOTOP,.QQ[VALTYPE],0,.QQ);
		END ELSE
		QQ_.QQ[ARG1PTR];
		CNODE[ARG1PTR]_.QQ;
		!SET DEFINITION POINT OF NODE JUST SUBSTITUTED
		CNODE[DEFPT2]_.PHI[STPT];
		!NOW, DEPENDING ON WHETHER OR NOT QQ IS A 
		!DATAOPR OR CMNSUB SET DEFPT1, VALFLAGS AND
		!PARENT POINTER

		IF .QQ[OPRCLS] EQL DATAOPR OR .QQ[OPRCLS] EQL CMNSUB THEN
		BEGIN
			CNODE[DEFPT1]_.TS;
			CNODE[A1VALFLG]_1;
		END ELSE
		BEGIN
			!AN EXPRESSION
			CNODE[DEFPT1]_.QQ[DEFPT1];
			QQ[PARENT]_.CNODE;
		END;
		!MAKE SURE QQ POINTS TO CNODE PARENT BEFORE
		!RETURN AS THIS FEATURE IS USED BY NEXTUP
		!IN THIS SKEWED CASE THE EXPRESSION WE WANT
		!TO EXAMINE IS THE ONE IN WHICH THE SUBSTITUTION HAS
		!OCCURRED.
		QQ_.CNODE;
	END ELSE
	BEGIN
		!HERE, ONCE AGAIN, WE HAVE THE SPECAIL LOCAL (SINGLE VARIABLE) CASE.
		!CNODE IS A POINTER TO THE EXPRESSION AND WILL
		!ITSELF FUNCTION AS THE PARENT
		!NO OTHER CASE LOOKS FOR COMMON SUBS IN AN ARRAYREF
		!OR RELATIONAL.****THIS IS AN IMPORTANT CONCEPT****.
		!THE GLOBAL CASE  DOES LOOK FOR RELATIONALS SO AN
		!ADDITIONAL TEST ON BACKST IS ALSO NECESSARY

		!SAVE VALUE OF T
		OLDT_.T;
		IF (.CNODE[OPRCLS] EQL ARRAYREF OR
		.CNODE[OPRCLS] EQL RELATIONAL)  AND .BACKST NEQ 0 THEN
		BEGIN
			QQ_.CNODE;
			CNODE_.PHI[LKER];
			!IF IT IS A RELATIONAL WE MUST
			!SET A SPECIAL FLAG SO THAT
			!THE REGISTER ALLOCATOR WILL KNOW THAT
			!IT MAY HAVE TO BE MOVED TO ANOTHER
			!REGISTER IF THIS IS AN AOBJN LOOP.
			IF .QQ[OPRCLS] EQL RELATIONAL THEN
				T[CSFULLWDFLG]_1;
		END ELSE
		BEGIN
			QQ_.CNODE[PARENT];
			!IF T IS A VARIABLE (THIS IS THE GLOBAL CASE9
			!DONT SET THE PARENT TO T. DONT SET THE PARENT
			!AT ALL.
			IF .T[OPRCLS] EQL CMNSUB THEN
				CNODE[PARENT]_.T;
		END;

		IF .NAN THEN NEGT_
			MAKPR1(.QQ,NEGNOT,NEGOP,.CNODE[VALTYPE],0,.T);
		IF .QQ[OPRCLS] EQL STATEMENT THEN
		BEGIN
			IF .NAN THEN
			IF SETNEG(.QQ,0) THEN ELSE
				(T_.NEGT;FLAG_1;);
			REPLACARG(.QQ,.CNODE,.T);
			IF .T[IDDOTO] EQL SIXBIT ".O" THEN
				IF .QQ[SRCID] EQL ASGNID THEN
					IF .QQ[SRCOPT] NEQ 0 THEN
						QQ[OPDEF]_.PHI[STPT];
		END
		ELSE
		IF .QQ[OPRCLS] EQL FNCALL THEN
		BEGIN
			LOCAL ARGUMENTLIST AG;
			IF .NAN THEN (T_.NEGT;FLAG_1;);
			AG_.QQ[ARG2PTR];
			!SET UP PARMS INCASE WE HAVE
			!TO CALL LEAFSUBSTITUTE TO LOCATE IT
			ITMCT_1;
			GLOBREG[0]_.CNODE;
			CHOSEN[0]_.T;
			SPECCASE_0;
			LOKCALST(.AG,.AG[ARGCOUNT],.CNODE,.T);
			!PUT DEFINITION POINT INTO NODE IF
			!APPROPRIATE
			IF ARGCONE(.QQ) THEN
				QQ[DEFPT2]_.PHI[STPT];
		END ELSE
!**;[620], CMNLNK @4431, DCE, 22-SEP-77
!**;[620], IF THIS IS AN IOLSCLS NODE, WE HAVE TO BE VERY CAREFUL
!**;[620], WITH WHERE WE TIE IN THE POINTER!
%[620]%		IF .QQ[OPRCLS] EQL IOLSCLS THEN
%[620]%		BEGIN
%[620]%			IF .NAN THEN (T_.NEGT; FLAG_1);
%[620]%			IF .QQ[SCALLELEM] EQL .CNODE
%[620]%				THEN QQ[SCALLELEM]_.T 
%[620]%			ELSE IF .QQ[SCALLCT] EQL .CNODE
%[620]%				THEN QQ[SCALLCT]_.T;
%[620]%		END ELSE
		IF .QQ[ARG1PTR] EQL .CNODE AND .QQ[OPRCLS] NEQ ARRAYREF THEN
		BEGIN
			IF .NAN THEN
				IF SETNEG(.QQ,1) THEN ELSE
					(T_.NEGT;FLAG_1;);
			QQ[ARG1PTR]_.T;
			IF .T[OPRCLS] EQL CMNSUB OR .T[OPRCLS] EQL DATAOPR THEN
				QQ[A1VALFLG]_1;
			QQ[DEFPT1]_.PHI[STPT];
		END ELSE
		BEGIN
			IF .NAN THEN
				IF SETNEG(.QQ,0) THEN ELSE
					(T_.NEGT;FLAG_1;);
			QQ[ARG2PTR]_.T;
			IF .T[OPRCLS] EQL CMNSUB OR .T[OPRCLS] EQL DATAOPR THEN
				QQ[A2VALFLG]_1;
			QQ[DEFPT2]_.PHI[STPT];
		END;

		!ONE MORE THING. IF THE PARENT IS
		!A CONTROL TYPR BOOLEAN WE HAVE TO CHANGE IT
		!TO CONTROL, CUZ CODE GENERATION CANNOT HANDLEE
		!A VALUE UNDER A BOOLEAN OR TYPE CONTROL
		T_.QQ;
		ADJCTL:
		WHILE .T[OPRCLS] EQL BOOLEAN AND .T[VALTYPE] EQL CONTROL
		DO
		BEGIN
			!CHANGE TO LOGICAL
			T[VALTYPE]_LOGICAL;
			!LOOK AT NEXT PARENT
			T_.T[PARENT];
			!CHECK FOR ORPHAN
			IF .T EQL 0 THEN LEAVE ADJCTL;
		END;

		!RESTORE NODE SPACE FREED, IF ANY
		IF .NAN THEN
			IF .FLAG THEN ELSE SAVSPACE(EXSIZ-1,.NEGT);
		T_.OLDT;
		IF .T[OPRCLS] EQL CMNSUB THEN
		BEGIN
			!THE CHECK FOR THE VALFLG BEING SET ON
			!A CMNSUB NODE IS EQUIVALENT TO THE
			!SPECIAL CASE CHECK FOR THE LOCAL
			!UNARY SUBSCRIPT OR RELATIONAL.
			!IT IS CLEAR THAT THE SPACE FOR A
			!SYMBOL TABLE NODE DOES NOT GET FREED.
			IF .T[ARG2PTR] NEQ .CNODE
			AND NOT .T[A2VALFLG] THEN
				SAVSPACE(EXSIZ-1,.CNODE);
		END;
	END;
	.QQ
END;

ROUTINE CHKHAIR(CNODE,PHI,SHAPE)=
BEGIN
	LOCAL BASE ARGNODE;
!
!CHECK NODE FOR HAVING ANOTHER COMMON SUB-EXPRESSION UNDER IT
!IF IT DOES SET CMNUNDER FLAG IN HASH TABLE NODE.

	EXTERNAL TOP,LENTRY;
	MAP BASE TOP:PA;
	MAP PEXPRNODE CNODE:PHI;
MACRO ARG1CHK=
BEGIN
	ARGNODE_.CNODE[ARG1PTR];
	IF .ARGNODE[OPRCLS] EQL CMNSUB OR .ARGNODE[IDDOTO] EQL SIXBIT".O" THEN
		PHI[CMNUNDER]_1;
END$;
MACRO ARG2CHK=
BEGIN
	ARGNODE_.CNODE[ARG2PTR];
	IF .ARGNODE[OPRCLS] EQL CMNSUB OR .ARGNODE[IDDOTO] EQL SIXBIT".O" THEN
		PHI[CMNUNDER]_1;
END$;
MACRO CHKBOTH=
BEGIN
	ARG2CHK;
	IF .SHAPE EQL SKEW THEN
	BEGIN
		CNODE_.CNODE[ARG1PTR];
		ARG2CHK;
	END ELSE
		ARG1CHK;
END$;



	CASE .CNODE[OPRCLS] OF SET

	!BOOLEAN
	BEGIN
		CHKBOTH;
	END;
	!DATAOPR
		RETURN;
	!RELATIONAL
	BEGIN
		CHKBOTH;
	END;
	!FNCALL
		RETURN;
	!ARITHMETIC
	BEGIN
		CHKBOTH;
	END;
	!TYPCNV
		ARG2CHK;
	!ARRAYREF
		RETURN;
	!CMNSUB
		RETURN;
	!NEGNOT
		ARG2CHK;
	!SPECOP
		ARG1CHK;
	!FIELDREF
		RETURN;
	!STORECLS
		RETURN;
	!RECONTENTS
		RETURN;
	!LABOP
		RETURN;
	!STATEMENT
		RETURN;
	!IOLSCLS
		RETURN;
	!INLINFN
	BEGIN
		ARG1CHK;
		IF .CNODE[ARG2PTR] NEQ 0 THEN
			ARG2CHK;
	END

	TES;
END;

MACRO
REVARG=
ENTRYP[HA1]_.CNODE[ARG2PTR];
ENTRYP[HDEF1]_.CNODE[DEFPT2];
ENTRYP[HA2]_.CNODE[ARG1PTR];
ENTRYP[HDEF2]_.CNODE[DEFPT1];$,

REGARG=
ENTRYP[HA1]_.CNODE[ARG1PTR];
ENTRYP[HDEF1]_.CNODE[DEFPT1];
ENTRYP[HA2]_.CNODE[ARG2PTR];
ENTRYP[HDEF2]_.CNODE[DEFPT2];$,

SREVARG=
ENTRYP[HA1]_.CNODE[ARG2PTR];
ENTRYP[HDEF1]_.CNODE[DEFPT2];
ENTRYP[HA2]_.QQ[ARG2PTR];
ENTRYP[HDEF2]_.QQ[DEFPT2];$,

SREGARG=
ENTRYP[HA1]_.QQ[ARG2PTR];
ENTRYP[HDEF1]_.QQ[DEFPT2];
ENTRYP[HA2]_.CNODE[ARG2PTR];
ENTRYP[HDEF2]_.CNODE[DEFPT2];$;

!
!********************************************
!
OWN TALLY;
GLOBAL ROUTINE HASHIT(CNODE,SHAPE)=
BEGIN
	EXTERNAL LOOPNO,REDEFPT;
	OWN PHAZ2 ENTRYP;
	MAP PHAZ2 CNODE;
	!CREATE HASH TABLE ENTRY FOR LOOKUP; THE GLOBAL ENTRY IS USED.
	!ENTRY IS FILLED WITH THE HASH KEY ELEMENTS. THESE ARE
	!THE OPERATOR, ARGUMENTS(S) AND DEFINITION POINTS.
	!THE MACROS PRECEEDING THIS ROUTINE HELP ASSURE THAT
	!ARGUMENTS ARE IN THEIR PROPER ORDER.
	!NODE:*******NO DOT ON ASSIGNMENT TO ENTRYP
	ENTRY_0;  ENTRYP_ENTRY;
	ENTRY+1_0; 
	NAN_0;
	IF .FLGREG<OPTIMIZE>  AND NOT .IMPLDO THEN
			REDEFPT(.CNODE,.SHAPE);
	ENTRYP[BLKID]_.LOOPNO;
	CASE .SHAPE OF SET
	!
	!UNARY
	!
	!TYPECNV, ARRAYREF WITH SIMPLE VARIABLE AS RESULTANT SUBSCRIPT
	!ALSO LIBRARY FUNCTIONS OF A SINGLE ARGUMENT
	!ALSO SPECIAL OPERATORS WHICH ARE ARG1 TYPES INSTEAD OF ARG2.
		BEGIN
			!IF THE ITEM IS A VARIABLE (THIS IS SPECIAL ARRAYREF CASE
			IF .CNODE[OPRCLS] EQL DATAOPR AND .CNODE[OPERSP] NEQ CONSTANT  THEN
			BEGIN
				ENTRYP[HOP]_VARFL;
				ENTRYP[HDEF1]_0;
				ENTRYP[HA2]_.CNODE;
				ENTRYP[HDEF2]_0;
				ENTRYP[HA1]_0;
			END ELSE
			!IT IS NOT THE LOCAL SPECIAL CASE
			!SEE IF ITS AN ARRAYREF (GLOBAL ONLY)
			IF .CNODE[OPRCLS] EQL ARRAYREF THEN
			BEGIN
				ENTRYP[HOP]_.CNODE[OPERATOR];
				!FUDGE THE OFFSET INTO THE BLOCK ID
				ENTRYP[BLKID]_.CNODE[TARGADDR];
				!BUT BE SURE THE EMPTY BIT IS OFF
				ENTRYP[EMPTY]_0;
				REGARG;
			END 
			ELSE
			!NOT AN ARRAYREF, TRY FUNCTION REFERENCE
			IF .CNODE[OPRCLS] EQL FNCALL THEN
			BEGIN
				REGISTER ARGUMENTLIST AG;
				ENTRYP[HOP]_.CNODE[OPERATOR];
				ENTRYP[HA1]_.CNODE[ARG1PTR];
				ENTRYP[HDEF1]_0;
				AG_.CNODE[ARG2PTR];
				ENTRYP[HA2]_.AG[1,ARGNPTR];
				ENTRYP[HDEF2]_.CNODE[DEFPT2];
			END ELSE
			!NOT A FUNCTION CALL EITHER. CHECK FOR
			!SPECIAL OPERATOR
			IF .CNODE[OPRCLS] EQL SPECOP THEN
			BEGIN
				IF .CNODE[A1NEGFLG] THEN
					NAN_1;
				ENTRYP[HOP]_.CNODE[OPERATOR]+.CNODE[A1NOTFLG];
				REGARG;
			END ELSE
			!NOW TREAT  EVERYONE THE SAME
			!(TYPECNV AND NEGNOT
			BEGIN
				IF .CNODE[A2NEGFLG] THEN NAN_1;
				ENTRYP[HOP]_.CNODE[OPERATOR]+.CNODE[A2NOTFLG];
				REGARG;
			END;

		END;
		!
		!STRAIGHT
		!
		!      OP
		!    *    *
		!  *        *
		!DATA     DATA
		BEGIN
			IF .CNODE[OPRCLS] EQL ARITHMETIC THEN
			BEGIN
			!ARITHMETIC NODES ARE A SPECIAL CASE
			!BECAUSE THE SKELETON OPTIMIZER HAS
			!ELIMINATED SUBTRACT NODES IN FAVOR OF
			!ADDS WITH PROPER NEG FLAGS SET. WE
			!ALSO WISH TO RECOGNIZE A-B AND B-A AS
			!THE SAME EXPRESSION (PLUS NEGATE ON ONE OF THEM).

			!TALLY IS THE XOR OF THE NEGATE FLAGS
			!PRESENT IN THE NODE.
			TALLY_.CNODE[A1NEGFLG] XOR .CNODE[A2NEGFLG];
			IF .CNODE[OPR1] EQL ADDOPF THEN
			!ADDS ARE A SPECIAL CASE
			BEGIN
				!IN ALL CASES THE EXPRESSION WILL BE
				!CONSIDERED TO BE A-B. THIS IS
				!TALLY =1 AND A2NEGFLG SET. TALLY = 1
				!AND A1NEGFLG SET IS B-A WHICH NEEDS
				!A NEGATION (NAN SET).
				!  0	  NAN
				!-----	-----
				!A+B	-(A+B)
				!A-B	-A+B==B-A

				NAN_.CNODE[A1NEGFLG];

				ENTRYP[HOP]_.CNODE[OPERATOR]+.TALLY+
				.CNODE[A1NOTFLG]^1+.CNODE[A2NOTFLG]^2;

				!MAKE SURE ONCE AGAIN THAT ARGS ARE
				!IN THE CORRECT ORDER. IN GENERAL,
				!THEY ARE PROPERLY ORDERED BY CANONICALIZATION
				!WHEN NEWLY FOUND COMMON SUBS ARE INVOLVED
				!WE MAY NEED TO ORDER THEM HERE.
				IF .CNODE[ARG1PTR] GTR  .CNODE[ARG2PTR] THEN
				BEGIN
					REVARG;
					IF .TALLY THEN NAN_NOT .NAN;
				END ELSE 	!ARGS ARE ALREADY IN RIGHT ORDER
				BEGIN
					REGARG;
				END;
			END ELSE	!END OF ADD OPERATION CASE

			BEGIN
				NAN_.TALLY;
				ENTRYP[HOP]_.CNODE[OPERATOR]+
				.CNODE[A1NOTFLG]^1+.CNODE[A2NOTFLG];
				!MULTIPLY IS ALSO SOMEWHAT SPECIAL
				IF .CNODE[OPR1] EQL MULOPF THEN
				BEGIN
					!CHECK ARG ORDER AGAIN
					IF .CNODE[ARG1PTR] GTR .CNODE[ARG2PTR] THEN
					BEGIN
						REVARG;
					END ELSE	!ARGS IN PRDER
					BEGIN
						REGARG;
					END;
				END ELSE	!NOT A MULTIPLY
				BEGIN
					REGARG;
				END;
			END;	!END OF NOT ADD BUT STILL ARITHMETIC
			END ELSE	!END OF ARITHMETIC
			BEGIN
				NAN_0;
				ENTRYP[HOP]_.CNODE[OPERATOR]+
				.CNODE[A1NOTFLG]^1+.CNODE[A2NOTFLG];
				REGARG;
			END;
		END;	!END OF STRAIGHT CASE
	!
	!SKEWED TREES
	!
	!      OP(CNODE)
	!    *    *
	!  *        *
	!OP(QQ)    DATA
	!   *
	!     *
	!    DATA
	!
	BEGIN
		QQ_.CNODE[ARG1PTR];
		IF .CNODE[OPRCLS] EQL ARITHMETIC THEN
		BEGIN	!ARITHMETIC NODE
			!TALLY CONTAINS THE NUMBER OF NEGATIVES ON THE EXPRESSION
			TALLY_.CNODE[A2NEGFLG] XOR .QQ[A2NEGFLG];
			IF .CNODE [OPR1] EQL ADDOPF THEN
			BEGIN	!AN ADD
				ENTRYP[HOP]_.CNODE[OPERATOR]+.TALLY
				+.CNODE[A1NOTFLG]^1+.CNODE[A2NOTFLG]^2;
				!TALLY AND A NEGATE (GLAG) ON THE SECOND
				!NODE DETERMINE IF A NEGATE IS NEEDED
				!ON THE COMMON SUB.
				NAN_.QQ[A2NEGFLG];
				!INSURE ARGS ARE ORDERED BY SYMBOL TABLE ADDRESS
				IF .QQ[ARG2PTR] GTR .CNODE[ARG2PTR] THEN
				BEGIN	!THEY NEED SWITCHING
					SREVARG;
					IF .TALLY THEN NAN_NOT .NAN;
				END ELSE	!THEY DONT NEED SWITCHING
				BEGIN
					SREGARG;
				END;
			END ELSE	!NOT AN ADD
			BEGIN
				NAN_.TALLY;
				ENTRYP[HOP]_.CNODE[OPERATOR]
				+.CNODE[A1NOTFLG]^1+.CNODE[A2NOTFLG];
				IF .CNODE[OPR1] EQL MULOPF THEN
				BEGIN	!A MULTIPLY
					!WORRY ABOUT ARG ORDER AGAIN
					IF .QQ[ARG2PTR] GTR .CNODE[ARG2PTR] THEN
					BEGIN
						!NEED REORDERING
						SREVARG;
					END ELSE
					BEGIN	!NO REORDERING NEEDED
						SREGARG;
					END
				END ELSE	!NOT MULTIPLY
				BEGIN
					SREGARG;
				END
			END;
		END ELSE	!END ARITHMETIC
		BEGIN	!NOT ARITHMETIC
			ENTRYP[HOP]_.CNODE[OPERATOR]
!**;[566], HASHIT @4816 (IN NOT ARITHMETIC), DCE, 4-MAY-77
!**;[566], CHECK ON THE CORRECT FLAGS FOR HASHING UNIQUELY
%[566]%			+.QQ[A2NOTFLG]^1+.CNODE[A2NOTFLG];
			SREGARG;
		END;
	END	!END SKEWED TREE CASE
	TES;
END;				!HASHIT
!****************************************
EXTERNAL CHKDOMINANCE;
ROUTINE XPUNGE(CNODE,SHAPE)=
BEGIN
	LABEL FIND;
	EXTERNAL BACKST;
	!TREE HAS BEEN WALKED TO THE LEAF*OPERATOR*LEAF
	!POINT. THE EXPRESSION WILL NOW BE HASHED, ETC.

	MAP PEXPRNODE CNODE;
	!THE LOCAL AND GLOBAL CASES ARE DISTINGUISHED HERE
	!BY A TEST ON THE VALUE OF BACKST. BACKST MUST BE
	!ZERO IN THE GLOBAL CASE. THE CHECK IS NECESSARY SINCE
	!THE GLOBAL CASE REQUIRES MUCH CHECKING NOT NECESSARY IN THE
	!LOCAL CASE.

	IF .BACKST NEQ 0 OR .IMPLDO THEN	!LOCAL CASE OR
						!I/O OPTIMIZER CASE
	BEGIN
	!**;[427], XPUNGE @4777, DCE, 19-AUG-76
	!**;[427], WE CANNOT HANDLE SHAPES GREATER THAN SKEW HERE,
	!**;[427], FOR THERE IS NO SUCH CODE IN HASHIT.  TO ADD
	!**;[427], CODE FOR THIS CASE, WE COULD USE CHKDOMINANCE AS
	!**;[427], A TEMPLATE, BUT THE CODE IS EXTENSIVE.  FOR NOW
	!**;[427], SIMPLY DO NOT ATTEMPT TO HANDLE CASES WITH
	!**;[427], SHAPE GREATER THAN SKEW.  AN EXAMPLE
	!**;[427], IS: READ() (A(B(I),J),C(B(I),J),J=1,10)
	%[427]%	IF .SHAPE GTR SKEW THEN RETURN;
		HASHIT(.CNODE,.SHAPE);
		PHI_TBLSRCH();
		IF .FLAG THEN MATCHER(.CNODE,.SHAPE,.NAN,.PHI)
		ELSE
		BEGIN
			PHI_
			MAKETRY(.PHI,.CNODE,.SHAPE);
			IF .SHAPE EQL SKEW THEN
			PHI[NBRCH]_1;
			IF .NAN THEN
			PHI[NEDSANEG]_1;
	!**;[405], XPUNGE @4781, DCE, 28-MAY-76
	!**;[405], FIND DEFINITION POINTS CORRECTLY FOR STAR1 AND
	!**;[405], STAR2 CASES.  THIS MAY HAVE TO BE CHANGED IF
	!**;[405], MORE GENERAL EXPRESSIONS ARE ALLOWED IN I/O LISTS
	!**;[405], FOR NOW IT CATCHES THE CASE (A(P(I)),I=1,4) WHERE
	!**;[405], A IS A FORMAL PARAMETER BEING PASSED
	%[405]%		IF NOT .IMPLDO THEN PHI[STPT]_0 !LOCAL CASE
	%[405]%		ELSE BEGIN  ! I/O LIST CASE

	%[405]%		LOCAL CN,DF1,DF2;
	%[405]%		MAP PEXPRNODE CN;
	%[405]%		PA_.LENTRY;

	!**;[405], HERE IS THE MAIN CHANGE - WE MUST DROP DOWN ONE
	!**;[405], NODE PRIOR TO GRABBING THE DEF POINTS FOR STAR1
	!**;[405], AND STAR2 SHAPES - CN POINTS TO THE NODE WE WANT

	%[405]%		CN_IF .SHAPE EQL STAR1 THEN .CNODE[ARG1PTR]
	%[405]%			ELSE IF .SHAPE EQL STAR2 THEN .CNODE[ARG2PTR]
	%[405]%			ELSE .CNODE;
	%[405]%		DF1_.CN[DEFPT1]; DF2_.CN[DEFPT2];
	!**;[602], XPUNGE @4881, DCE, 9-AUG-77
	!**;[602], IF SHAPE IS SKEW, WE NEED TO GET THE CORRECT DEFINITION
	!**;[602], POINT FOR THE LEFT HAND NODE.  AN EXAMPLE CASE WHICH
	!**;[602], CAUSES THIS TO HAPPEN IS: A(B(L+I-1)),I=J,K
	!**;[602], WHERE B IS A FORMAL ARRAY!
	%[602]%		IF .SHAPE EQL SKEW
	%[602]%			THEN (CN_.CNODE[ARG1PTR];
	%[602]%				DF1_.CN[DEFPT2]);
	%[405]%		IF .DF1 EQL .DF2 THEN PHI[STPT]_.DF1 !DONE
	%[405]%		ELSE BEGIN
	%[405]%			P_IF .DF1 EQL 0 THEN 1 ELSE 0;
	%[405]%			IF .DF2 EQL 0 THEN P_.P+2;

	%[405]%		FIND:	WHILE 1 DO
	%[405]%			BEGIN

	%[405]%				IF NOT .P<0,1> THEN
	%[405]%					IF .PA EQL .DF1 THEN P_.P+1;
	%[405]%				IF NOT .P<1,1> THEN
	%[405]%					IF .PA EQL .DF2 THEN P_.P+2;
	%[405]%				IF .P EQL 3 THEN LEAVE FIND;
	%[405]%				PA_.PA[CLINK]
	%[405]%			END;
	%[405]%			PHI[STPT]_.PA
	%[405]%		END
	%[405]%	   END
	%[405]%	END
	END ELSE CHKDOMINANCE(.CNODE,.SHAPE);	!GLOBAL CASE
END;
!
!****************************************************
!
!
!***************************************************
!

FORWARD LOCELMIO;
ROUTINE ELIM(STMT)=
BEGIN
	MAP PHAZ2 STMT;
EXTERNAL IOGELM;		!WALK I/O LISTS <IOPT>
EXTERNAL IOCLEAR;		!COLLAPSE I/O LISTS IF GCALLSLFLG IS SET
EXTERNAL CSTMNT,BACKST,TOP,LEND,LOOP;
	MAP BASE TOP;
	MAP BASE BACKST;
	!CONTROLLING ROUTINE AT THE STATEMENT LEVEL FOR
	!COMMON SUB-EXPRESSION ELIMINATION, BOTH GLOBAL
	!AND LOCAL. ONLY STATEMENTS MENTIONED EXPLICITLY
	!IN THIS ROUTINE CAN EVEN POTENTIALLY HAVE
	!COMMON SUB-EXPRESSIONS.THOSE STATEMENT TYPES ARE:
	!ASSIGNMENT,LOGICAL IF, DO, ARITHMETIC IF,READ, WRITE.


	!ASSIGNMENT STATEMENTS
	IF .STMT[SRCID] EQL ASGNID THEN
	BEGIN
		PAE_.STMT[LHEXP];
		IF .PAE[OPRCLS] EQL ARRAYREF THEN
		REA(.PAE);
		!SPECIAL CHECK FOR VARIABLE INITIALIZATION IN THE GLOBAL
		!CASE
		IF .STMT[A2VALFLG] THEN
		BEGIN
			IF .FLGREG<OPTIMIZE> THEN
				IF .STMT[SRCOPT] NEQ 0 THEN
					IF .LOOP EQL 0 THEN
						IF .STMT[OPDEF] EQL .LENTRY THEN
					CHKINIT(.STMT[RHEXP]);
		END ELSE
		REA(.STMT[RHEXP]);
	END;

	!LOGICAL IF
	IF .STMT[SRCID] EQL IFLID THEN
	BEGIN
		REA(.STMT[LIFEXPR]);
		IF NOT .FLGREG<OPTIMIZE> THEN
		BEGIN
			!THE SPECIAL CHECK IS NECESSARY TO
			!AVOID PROCESSING THE STATEMENT
			!FOLLOWING THE LOGICAL IF TWICE IN THE
			!GLOBAL CASE. THE TRUE BRANCH IS ON
			!THE PROCESSING LIST AS A SPEARATE ENTITY
			!BY ITSELF IN THE GLOBAL CASE.

			!HOOK ALL LOCALS FOUND SO FAR TO THE
			!IF PART

			LOCLDEPD();
			STMT[SRCCOMNSUB]_.BACKST[SRCLINK];
			BACKST[SRCLINK]_0;
			LOCLNK_0;

			CSTMNT_.STMT[LIFSTATE];
			ELIM(.STMT[LIFSTATE]);
		END;
	END;

	!DO STATEMENT
	IF .STMT[SRCID] EQL DOID THEN
	BEGIN
		REA(.STMT[DOLPCTL]);
	END;

	!ARITHMETIC IF
	IF .STMT[SRCID] EQL IFAID THEN
	BEGIN
		REA(.STMT[AIFEXPR]);
	END;

	!I/O STATEMENTS
	IF .STMT[SRCID] GEQ READID AND .STMT[SRCID] LEQ REREDID THEN
%[V5]%	 IF .FLGREG<OPTIMIZE> AND NOT .GLOBELIM2 THEN
		IOGELM(.STMT)
	ELSE
		IF .GCALLSLFLG THEN
			IOCLEAR(.STMT)
		ELSE
			LOCELMIO(.STMT);
END;			!ELIM
!
!***************************************************
!
!
EXTERNAL BACKST; MAP PEXPRNODE BACKST;
!

OWN SAVCSTMNT;
GLOBAL ROUTINE LOCELIM(STMT)=
BEGIN
EXTERNAL CSTMNT,LOOPNO;
MAP PEXPRNODE STMT:CSTMNT;
!************************************
!CONTROL FOR LOCAL COMMONSUB-EXPRESSION ELIMINATION
!**************************************
	SAVCSTMNT_.CSTMNT;
	LOCLNK_0;
	STHASCMN_0;
	BACKST[SRCLINK]_0;
	LOOPNO_.CSTMNT[SRCISN];
	ELIM(.STMT);
	IF .STHASCMN THEN
	BEGIN
		LOCLDEPD();
		CSTMNT[SRCOPT]_.BACKST[SRCLINK];
	END;
	CSTMNT_.SAVCSTMNT;

	!THE ABOVE STATEMENT EITHER ZEROES THE POINTER TO COMMON
	!SUB-EXPRESSIONS OR SETS IT TO POINT TO THEM CORRECTLY


END;
!
!********************************************************
!
EXTERNAL EHASH; 
ROUTINE REA(STKPAE)=
!PAE IS AN EXPRESSION POINTER
BEGIN
!THIS ROUTINE IS NAMED IN HONOR OF REA RAILWAY EXPRESS.
!THIS IS WHERE WE ATTEMPT TO RAILROAD EVERYTHING THROUGH!
!***********************************
!
!AS IS NOT OBVIOUS IT DEALS WITH COMMON EXPRESSION ELIMINATION.
!IT DOES THE BASIC TREE WALK THROUGH THE TREES.
!IT IS CALLED BY ELIM AND CALLS XPUNGE TO HASH AND MATCH
!THE PHILOSOPHY BEHIND EACH SECTON OF CODE IS THE SAME.
!WALK THE TREE BASED ON THE SETTING OF THE VALFLGS (SAYS
!NODE UNDER HERE IS LEAF IS SET). WALK BRANCHES BEFORE LOOKING
!AT CURRENT NODE ITSELF.
!ALSO CHECK FOR THE SKEWED TREE CASE.

EXTERNAL FNARRAY;
REGISTER PHAZ2 PAE;
PAE_.STKPAE;
STHASCMN_1;
CASE .PAE[OPRCLS] OF SET
!
!BOOLEAN
!
	BEGIN
			IF NOT .PAE[A1VALFLG] THEN REA(.PAE[ARG1PTR]);
			IF NOT .PAE[A2VALFLG] THEN REA(.PAE[ARG2PTR]);
			IF .PAE[A1VALFLG] AND .PAE[A2VALFLG] THEN XPUNGE(.PAE,STGHT)
			ELSE
			BEGIN
				
				QQ_.PAE[ARG1PTR];
				IF .QQ[OPERATOR] EQL .PAE[OPERATOR] 
				AND 		!N-ARY WITH LEAVES
				.QQ[A2VALFLG] AND .PAE[A2VALFLG] 
					AND NOT .QQ[PARENFLG] THEN
				XPUNGE(.PAE,PSKEW);
			END;			!ELSE PART SKEWED TREE
	END;				!BOOLEAN CLASS OPERATORS
!
!DATAOPR
!
		RETURN;		!DO NOTHING
!
!RELATIONAL
!
	BEGIN
		IF NOT .PAE[A1VALFLG] THEN REA(.PAE[ARG1PTR]);
		IF NOT .PAE[A2VALFLG] THEN REA(.PAE[ARG2PTR]);
		!LOCAL CASE TEST OB BACKST
		!GLOBAL CASE BACKST **MUST** BE ZERO (0)
		IF .BACKST NEQ 0 THEN
		BEGIN
			VARHOLDER_.PAE;
			IF .PAE[A1VALFLG] THEN
			BEGIN
				QQ_.PAE[ARG1PTR];
				IF .QQ[OPRCLS] EQL DATAOPR AND .QQ[OPERSP] NEQ CONSTANT THEN
					XPUNGE(.QQ,UNARY);
			END;
			IF .PAE[A2VALFLG] THEN
			BEGIN
				QQ_.PAE[ARG2PTR];
				IF .QQ[OPRCLS] EQL DATAOPR AND .QQ[OPERSP] NEQ CONSTANT  THEN
					XPUNGE(.QQ,UNARY);
			END;
		END ELSE
		!GLOBAL OPTIMIZER SHOULD FIND THEM
	
!**;[520], REA @5009 (5098 IN VERSION 5), DCE, 19-NOV-76
!**;[520], DO NOT CONSIDER FOR COMMON SUBEXPRESSIONS RELATIONAL
!**;[520], EXPRESSIONS INVOLVING NEG FLAGS.  TO ALLOW THIS WILL CAUSE
!**;[520], EXPRESSIONS LIKE A .GT. B   AND   -A .GT. B   TO BE
!**;[520], CONSIDERED AS COMMON SUBS - CLEARLY WRONG!
%[520]%		IF(.PAE[A1NEGFLG] OR .PAE[A2NEGFLG]) THEN RETURN ELSE
			IF .PAE[A1VALFLG] AND .PAE[A2VALFLG] THEN
				XPUNGE(.PAE,STGHT);
	END;


!
!FNCALL - FUNCTION CALL 
!
	BEGIN
		LOCAL ARGUMENTLIST TMP;
		!STEP THROUGH ARGUMENTS. EACH ARGEUMENT HAS THE FUNCTION
		!NODE AS PARENT
		TMP_.PAE[ARG2PTR];
			INCR I FROM 1 TO .TMP[ARGCOUNT] DO
			BEGIN
				QQ_.TMP[.I,ARGNPTR];
				REA(.QQ);
			END;
		!IF OPTIMIZING GO OFF AND TRY FOR ARRAY REFS TOO

		IF .FLGREG<OPTIMIZE> THEN
		BEGIN
			FNARRAY(.PAE);
			RETURN;
		END;

		!!TRY TO ELIMINATE LIBRARY FUNCTIONS WITH 1 ARGUMENT
		IF ARGCONE(.PAE) THEN XPUNGE(.PAE,UNARY);
	END;			!FNCALL
!
!ARITHMETIC
!
	BEGIN
		IF NOT .PAE[A1VALFLG] THEN REA(.PAE[ARG1PTR]);
		IF NOT .PAE[A2VALFLG] THEN REA(.PAE[ARG2PTR]);

		!TRY ARRAY REFERENCES
		IF .FLGREG<OPTIMIZE> THEN
		BEGIN
			IF NOT .PAE[A1VALFLG] THEN
				A1ARREF(.PAE);
			IF NOT .PAE[A2VALFLG] THEN
				A2ARREF(.PAE);
		END;

		!NOW REGULAR SKEWED
%[V5]%		IF .PAE[A1VALFLG] AND .PAE[A2VALFLG]
%[V5]%		  THEN BEGIN
%[V5]%		    IF .FLGREG<OPTIMIZE> AND
%[V5]%		       .PAE [OPERSP] NEQ DIVOP
%[V5]%		      THEN BEGIN	! X OP .R -> .R OP X
%[V5]%			MACRO  IDDOTR  =  0,3,24,12$;
%[V5]%			REGISTER BASE T1;
%[V5]%
%[V5]%			T1 _ .PAE [ARG2PTR];
%[V5]%			IF .T1 [IDDOTR] EQL SIXBIT ".R"
%[V5]%			  THEN BEGIN
%[V5]%			    SWAPARGS  (PAE);
%[V5]%			    T1 _ .PAE [DEFPT1];
%[V5]%			    PAE [DEFPT1] _ .PAE [DEFPT2];
%[V5]%			    PAE [DEFPT2] _ .T1;
%[V5]%			  END;
%[V5]%		      END;
%[V5]%		    XPUNGE  (.PAE, STGHT);
%[V5]%		  END
		ELSE
		BEGIN
			QQ_.PAE[ARG1PTR];
			IF .QQ[OPR1] EQL .PAE[OPR1] AND
			.PAE[OPR1] NEQ DIVOPF
			AND NOT .QQ[PARENFLG]
			AND		!N-ARY WITH LEAVES
			.QQ[A2VALFLG] AND .PAE[A2VALFLG] THEN
			XPUNGE(.PAE,PSKEW);
			!LOOK DOWN ONCE MORE
			IF .PAE[A1VALFLG] AND .PAE[A2VALFLG] THEN
				XPUNGE(.PAE,STGHT);
		END;
	END;
!
!TYPCNV
!
	BEGIN
		IF NOT .PAE[A2VALFLG] THEN REA(.PAE[ARG2PTR]);
		IF .PAE[A2VALFLG] THEN XPUNGE(.PAE,UNARY);
	END;
!
!ARRAYREF
!
	BEGIN
		IF .PAE[ARG2PTR] EQL 0 THEN RETURN;
		IF .PAE[A2VALFLG] AND .BACKST NEQ 0 THEN
		!SPECIAL CASE FOR LOCAL ONLY
		BEGIN
			VARHOLDER_.PAE;
			QQ_.PAE[ARG2PTR];
			!ITS A NON-CONSTANT LEAF. CONSTANT LEAVES SHOULD HAVE
			!BEEN FOLDED INTO THE OFFSET.
			IF .QQ[OPRCLS] EQL DATAOPR AND .QQ[OPERSP] NEQ CONSTANT  THEN
				!THE NEG AND/OR NOT FLAGS CANNOT BE SET.
				!WE ARE NOT PREPARED TO HASH THEM. IN
				!GENERAL THIS WILL NOT PREVENT MUCH CUZ THE
				!FLAGS DONT MAKE A LOT OF SENSE ON THE SUBSCRIPT
				!ANYWAY.
				IF NOT .PAE[A2NEGFLG] AND NOT .PAE[A2NOTFLG] THEN
					XPUNGE(.QQ,UNARY);
		END ELSE
		REA(.PAE[ARG2PTR]);
	END;
	!
	!CMNSUB
	!
		RETURN;		!SHOULDNT HAPPEN
	!
	!NEGNOT
	!
		BEGIN
			IF NOT .PAE[A2VALFLG] THEN REA(.PAE[ARG2PTR]);
			IF .PAE[A2VALFLG] THEN XPUNGE(.PAE,UNARY);
		END;
	!
	!SPECOP
	!
		BEGIN
			IF NOT .PAE[A1VALFLG] THEN REA(.PAE[ARG1PTR]);
			IF .PAE[A1VALFLG] THEN XPUNGE(.PAE,UNARY);
		END;
	!
	!FIELDREF
	!
		RETURN;		!NOT IN RELEASE 1
	!
	!STORECLS
	!
		RETURN;
	!
	!REGCONTENTS
	!
		RETURN;
	!
	!LABOP
	!
		RETURN;
	!
	!STATEMENT
	!
		RETURN;
	!
	!IOLSCLS
	!
		RETURN;		!SEE REAIO
	!
	!INLINFN
	!
	BEGIN
		IF NOT .PAE[A1VALFLG] THEN REA(.PAE[ARG1PTR]);
		IF .PAE[A1VALFLG] THEN
			IF .PAE[ARG2PTR] EQL 0 THEN
				XPUNGE(.PAE,UNARY);
		IF NOT .PAE[A2VALFLG] THEN
			IF .PAE[ARG2PTR] NEQ 0 THEN
				REA(.PAE[ARG2PTR]);
		IF .PAE[A1VALFLG] AND .PAE[A2VALFLG] THEN
			XPUNGE(.PAE,STGHT);
	END;
TES;
END;
ROUTINE REAIO(CLSTCALL)=
BEGIN
	!EXAMINE THEN IOLSTCALL, E1LISTCALL, E2LISTCALL
	!FOR EXPRESSIONS TO HASH
	!CLSTCALL IS A POINTER TO AN I/O LIST.
	!WALK THAT TREE LOOKING FOR EXPRESSIONS TO EXPUNGE.
	MAP BASE CLSTCALL;
	LOCAL BASE P;
	IF .CLSTCALL[OPRCLS] EQL STATEMENT THEN RETURN;

	STHASCMN_0;
	CASE .CLSTCALL[OPERSP] OF SET
	!DATACALL
	BEGIN						!LEGAL ONLY RECURSIVELY
		P_.CLSTCALL[DCALLELEM];
		IF .P[OPRCLS] NEQ DATAOPR THEN
		REA(.P)
	END;
	!
	!SLISTCALL
	BEGIN						!LEGAL ONLY RECURSIVELY
		!NOTHING TO DO
	END;
	!
	!IOLSTCALL
	BEGIN
		P_.CLSTCALL[IOLSTPTR];
		WHILE .P NEQ 0 DO
		BEGIN
			REAIO(.P);
			P_.P[SRCLINK];
		END;
	END;
	!
	!E1LISTCALL
	;	!RELEASE >1
	!E2LISTCALL
	;	!RELEASE >1
	TES;
END;							!REAIO
GLOBAL ROUTINE LOCELMIO(PO)=
!CONTROL FINDING OF COMMON SUB EXPRESSIONS IN THE LOCAL CASE
!(ONLY ONE DONE FOR RELEASE ONE) IN I/O LISTS. CALLED BU ELIM.
!CALLS REAIO TO WALK TREES
BEGIN
	MAP BASE PO;
	REGISTER BASE IOLSTT;
	EXTERNAL BACKST;
	MAP BASE BACKST;

	IF .BACKST EQL 0 THEN RETURN;
	!RESET THE LINKING POINTER FOR LOCAL GOMMON SUBS.
	!THIS PRECLUDES LOCELMIO FROM EVER BEING USED RECURSIVELY
	!(CORRECTLY, THAT IS).
	LOCLNK_0;

	!PO POINTS AT IO STATEMENT

	!IN RELEASE 1 THERE IS NEVER A COMMON SUB ON AN I/O
	!STATEMENT ITSELF SO WE WILL ZERO THE FIELD. THIS
	!ALSO HELPS MAKE SURE THE GLOBALLY USED FIELDS ARE CLEARED.
	PO[SRCOPT]_0;

	!ROUTINE DOES LOCAL ELIMINATION ON IOLSTCALL (RELEASE 1)
	!E1LISTCALL AND E2LISTCALL (RELEASE >1)
	IF .PO[IOLIST] NEQ 0 THEN
	BEGIN
		IOLSTT_.PO[IOLIST];
		WHILE .IOLSTT NEQ 0 DO
		BEGIN
			IF .IOLSTT[OPRCLS] NEQ STATEMENT THEN
			BEGIN
				IF .IOLSTT[OPERSP] EQL IOLSTCALL THEN
				BEGIN
					REAIO(.IOLSTT);
					LOCLDEPD();
					IOLSTT[SRCCOMNSUB]_.BACKST[SRCLINK];
					BACKST[SRCLINK]_0;
					LOCLNK_0
				END;
			END;
			IOLSTT_.IOLSTT[CLINK];
		END;
	END;
END;
!THE FOLLOWING FEW ROUTINES ARE UTILITY ROUTINES FOR DEALING WITH
!THE EXPRESSION HAS TABLE.

ROUTINE SLINGHASH=
BEGIN
	!CLEAN OUT THE EXPRESSION HASH TABLE

	MAP BASE PAE;

	DECR I FROM EHSIZ-1 TO 0 DO
	BEGIN
		PAE_.EHASH[.I];
		WHILE .PAE NEQ 0 DO
		BEGIN
			PAE[EMPTY]_1;
			PAE_.PAE[CLINK];
		END;
	END;
END;


GLOBAL ROUTINE TBLSRCH=
BEGIN
LABEL LOKER;
MAP PEXPRNODE P;
	LOCAL T;
	!LOOK UP AN EXPRESSION IN THE EXPRESSION HASH TABLE.
	!THE ROUITNE HASHIT HAS FILLED IN THE GLOBAL ENTRY
	!WITH THE PROPER PARAMETERS.
	!RETURNS FLAG IF FOUND 1
	!ELSE
	!POINTER TO ENTRY IF FOUND
	!USES GLOBALS ENTRY AND FLAG
	!IF FLAG IS SET TPREV POINTS TO
	!PREVIOUS ENTRY ON LIST IF ANY.  ZERO IF NONE
	T_ABS(.(ENTRY+2) XOR .(ENTRY+1)) MOD EHSIZ;
	EHASHP_EHASH[.T]<0,0>;
	IF .EHASH[.T] EQL 0 THEN
	BEGIN
		FLAG_0;
		TPREV_EHASH[.T]<0,0>;
		NEDCOR_1;
		RETURN(.TPREV);
	END ELSE
	P_.EHASH[.T];
	TPREV_.P;
	NEDCOR_0;
	LOKER:
	DO
	BEGIN
		IF .P[EMPTY] THEN LEAVE LOKER;
		PC_.P+1;
		IF @.PC EQL .(ENTRY+1) THEN
		BEGIN
			PC_.PC+1;
			IF @.PC EQL .(ENTRY+2) THEN
			BEGIN
			PC_.PC+1;
			IF @.PC EQL .(ENTRY+3) THEN
			BEGIN
				FLAG_1;
				RETURN(.P);
			END;
			END;
		END;
		TPREV_.P;
		P_.P[CLINK];
	END UNTIL .P EQL 0;		!EQL 0
	FLAG_0;
	IF .P EQL 0 THEN
		NEDCOR_1;
	RETURN(.TPREV);
END;

OWN THISBLK,MOREFLG;
GLOBAL ROUTINE MAKETRY (PLACE,CNODE,SHAPE)=
BEGIN
	EXTERNAL LOOPNO;
	OWN PHAZ2 ENTRYP;
	MAP PEXPRNODE CNODE;
	MAP PHAZ2 PLACE;
	!ENTERS AN ENTRY INTO HASH TABLE
	!PLACE POINTS TO WHERE IT GOES
	!ZERO MEANS WE NEED CORE FOR IT
	ENTRYP_ENTRY<0,0>;
	IF .NEDCOR THEN
	BEGIN
		NAME<LEFT>_6; PLACE_CORMAN();
		TPREV[CLINK]_.PLACE;
	END ELSE

	!IT IS POSSIBLE THAT PLACE POINTS TO A FULL ENTRY WHICH
	!POINTS TO AN EMPTY ENTRY. OBVIOUSLY, IT IS THE EMPTY
	!ENTRY THAT WE DESIRE TO USE.

	IF NOT .PLACE[EMPTY] THEN PLACE_.PLACE[CLINK];
	PLACE[USECNT]_1;
	PLACE[EMPTY]_0;
	PLACE[CMNFLGS]_0;
	PLACE[TEMPER]_0;
	PLACE[BLKID]_.ENTRYP[BLKID];
	PLACE[HOP]_.ENTRYP[HOP];
	PLACE[HA1]_.ENTRYP[HA1];
	PLACE[HA2]_.ENTRYP[HA2];
	PLACE[HDEF1]_.ENTRYP[HDEF1];
	PLACE[HDEF2]_.ENTRYP[HDEF2];
	PLACE[LKER]_.CNODE;

	!IF THIS IS AN ARRAYREF ZERO THE PARENT(LINK) FIELD
	IF .CNODE[OPRCLS] EQL ARRAYREF THEN
		CNODE[PARENT]_0;

	!THE SPECIAL CASE FOR LOCAL COMMON SUB-EXPRESSIONS OF A
	!SINGLE VARIABLE AS A SUBSCRIPT OF UNDER A RELATIONAL
	!SET TEMPER TO THE MODULE OWN VARHOLDER FOR LATER USE
	!IN UNRYMATCHER.
	!THIS IS THE ONLY PLACE WHERE A DATAOPR SHOULD OCCUR.
	IF .CNODE[OPRCLS] EQL DATAOPR THEN
		PLACE[TEMPER]_.VARHOLDER
	ELSE
	!LOOK TO SEE IF THIS ONE CONTAINS ANOTHER ONE.
		CHKHAIR(.CNODE,.PLACE,.SHAPE);
	.PLACE
END;

EXTERNAL PREV;
MAP PHAZ2 P:QQ:P1:PO:PREV;
GLOBAL ROUTINE DELETE(NOD,NUMB)=
BEGIN
	EXTERNAL PUTBACKARRAY;
	!**;[442], DELETE @5374, DCE, 9-SEP-76
	LOCAL TSAVE; ![442]
	LOCAL T;
	MAP PHAZ2 NOD;
	!**;[442], DELETE @5376, DCE, 9-SEP-76
	!**;[442], MAKE THE COMMENT REFLECT REALITY
	!TPREV POINTS TO PREVIOUS NODE OR NODE ITSELF INITIALLY [442]
	!DEPENDING ON IF THIS IS THE FIRST NODE IN ITS HASH LIST [442]
	!NOD POINTS TO ENTRY IN HASH TABLE
	!LINK TO BEGINNING OF EMPTY LIST
	!THE TEMP T IS NECESSARY TO INSURE A CORRECT NEGATIVE VALUE
	LABEL ENDLOK;

	T_.NOD[USECNT]-.NUMB;
	!IF IS BECAME UNUSED
	IF .T LEQ 0 THEN
	BEGIN

		NOD[EMPTY]_1;
		NOD[USECNT]_0;
		!IF OPTIMIZING WE MAY HAVE TO RECONSTRUCT AN
		!AN ARRAYREF NODE

		IF .FLGREG<OPTIMIZE> THEN
			PUTBACKARRAY(.NOD,(IF .NOD[NBRCH] THEN
						SKEW ELSE STGHT));

		PREV_.NOD[CLINK];		!PREV IS A TEMP
		IF .PREV EQL 0 THEN RETURN;
		IF .PREV[EMPTY] THEN RETURN;
			!LINK OUT ENTRY THAT BECAME EMPTY AND PUT IT AT
			!THE END OF THE LIST.
			!NOTE THAT TPREV WAS SET BY TBLSRCH TO POINT
			!TO THE ENTRY BEFORE NOD.

	!**;[442], DELETE @5404, DCE, 9-SEP-76
			TSAVE_TPREV[CLINK]_.PREV;![442]

			ENDLOK:
			WHILE 1
			DO			!LOOK FOR END OF LIST
			BEGIN
				TPREV_.PREV;
				PREV_.PREV[CLINK];
				IF .PREV EQL 0 THEN
				BEGIN
					TPREV[CLINK]_.NOD;
					NOD[CLINK]_0;
					LEAVE ENDLOK;
				END ELSE
				IF .PREV[EMPTY] THEN
				BEGIN
					NOD[CLINK]_.PREV;
					TPREV[CLINK]_.NOD;
					LEAVE ENDLOK;
				END;
			END;
				IF @.EHASHP EQL .NOD THEN
	!**;[442], DELETE @5425, DCE, 9-SEP-76
	!**;[442], MAKE THE HASH POINTER CORRECTLY POINT TO
	!**;[442], THE NEW FIRST ELEMENT IN THE LINKED LIST
	!+;;*[442], IN THE CASE THE DELETED ELEMENT WAS FIRST
	%[442]%				EHASH[.EHASHP-EHASH<0,0>]_.TSAVE;
	END			!ENTRY GOING EMPTY
	ELSE	!PUT NEW COUNT INTO NODE
		NOD[USECNT]_.T;

END;
	!HASH NODE
	!
	! -----------------------------------
	! *                *                *
	! *     USECNT     *     CLINK      *
	! *		   *		    *
	! -----------------------------------
	! *		   *		    *
	! *  BLKID	   *    HOP	    *
	! *		   *		    *
	! -----------------------------------
	! *		   *		    *
	! *    HA1	   *     HA2	    *
	! *		   *		    *
	! -----------------------------------
	! *		   *		    *
	! *    HDEF1	   *     HDEF2	    *
	! *  		   *		    *
	!------------------------------------
	! *                *                *
	! *     TEMPER     *      LKER      *
	! *		   *		    *
	! -----------------------------------
	! *		   *		    *
	! *   NBRCH	   *	STPT	    *
	! *		   *		    *
	! -----------------------------------

!			FIRST BIT OF BLKID IS 1 IF BLOCK DELETED
!
!**********************************
!
ROUTINE LOCLMOV(CNODE)=
BEGIN
	!CALLED ONLY IN THE LOCAL COMMON SUB-EXPRESSION ELIMINATION
	!CASE. TEMPER IN THIS CASE POINTS TO A COMMON SUB-
	!**EXPRESSION NODE**. A LINKED LIST OF SUCH IS MADE AT
	!LOCLNK, BACKST POINTS TO THE TOP OF THE LIST.


	MAP BASE CNODE;
	MAP BASE LOCLNK:BACKST;

	IF .LOCLNK EQL 0 THEN
	BEGIN
		LOCLNK_.CNODE;
		BACKST[SRCLINK]_.CNODE;
	END ELSE
	BEGIN
		LOCLNK[SRCLINK]_.CNODE;
		CNODE[SRCLINK]_0;
		LOCLNK_.CNODE;
	END;
END;
!
!

!
ROUTINE ARGCMN(ANODE,LG)=
BEGIN
	!LG IS LOCAL - GLOBAL SWITCH.
	!ANODE SHOULD BE EITHER A POINTER TO A CMNSUB NODE (LOCAL)
	!OR AN OPTIMIZER CREATED VARIABLE STARTING WITH .O (GLOBAL)
	!THE ROUTINE RETURNS 1 IF EITHER OF THESE CONDITIONS IS TRUE.
	!IF IT IS RETURNING A ONE THE GLOBAL QQ ALSO CONTAINS THE
%[V5]%	!OMOVDCNS & USECNT FIELDS OF THE SYMBOL TABLE ENTRY

	EXTERNAL QQ;
	MAP BASE ANODE:T;

	IF .LG THEN
	BEGIN
		IF OPTMP(ANODE) THEN
		BEGIN
%[V5]%			QQ<RIGHT> _ .ANODE [EXPRUSE];
%[V5]%			QQ<LEFT> _ .ANODE [OMOVDCNS];
			RETURN(1);
		END
		ELSE
			RETURN(0)
	END ELSE	!LG=0(LOCAL)
	BEGIN
		IF .ANODE[OPRCLS] EQL CMNSUB THEN
		BEGIN
			QQ_.ANODE[EXPRUSE];
			RETURN(1);
		END
		ELSE
			RETURN(0);
	END;

END;

ROUTINE LOK1SUBS(CNODE,LG)=
BEGIN
	!DETERMINE IF ARG1 OF CNODE IS A :
	!	CMNSUB NODE (IF LG=0)
	!	A .O TEMP (IF LG=1)
	!THIS IS THE CONTROLLING CASE. THE REAL WORK IS DONE BY ARGCMN.
	!THIS ROUTINE RETURNS THE LOGICAL VALUE RETURNED TO IT BY
	!ARGCMN.

	MAP PEXPRNODE CNODE;

	RETURN(

	CASE .CNODE[OPRCLS] OF SET

		ARGCMN(.CNODE[ARG1PTR],.LG);	!BOOLEAN
		0;				!DATAOPR
		ARGCMN(.CNODE[ARG1PTR],.LG);	!RELATIONAL
		0;				!FNCALL
		ARGCMN(.CNODE[ARG1PTR],.LG);	!ARITHMETIC
		0;				!TYPECNV
		0;				!ARRAYREF
		0;				!CMNSUB
		0;				!NEGNOT
		ARGCMN(.CNODE[ARG1PTR],.LG);	!SPECOP
		0;				!FIELDREF
		0;				!STORCLS
		0;				!REGCONTENTS
		0;				!LABOP
		0;				!STATEMENT
		0;				!IOLCLS
		ARGCMN(.CNODE[ARG1PTR],.LG)	!INLINFN
		TES);
END;

ROUTINE LOK2SUBS(CNODE,LG)=
BEGIN
	!FUNCTIONS EXACTLY THE SAME AS LOK1SUBS, EXCEPT ON
	!ARG2 OF CNODE.

	MAP BASE CNODE;

	RETURN(
	CASE .CNODE[OPRCLS] OF SET

		ARGCMN(.CNODE[ARG2PTR],.LG);	!BOOLEAN
		0;				!DATAOPR
		ARGCMN(.CNODE[ARG2PTR],.LG);	!RELATIONAL
		0;				!FNCALL
		ARGCMN(.CNODE[ARG2PTR],.LG);	!ARITHMETIC
		ARGCMN(.CNODE[ARG2PTR],.LG);	!TYPECNV
		0;				!ARRAYREF
		0;				!CMNSUB
		ARGCMN(.CNODE[ARG2PTR],.LG);	!NEGNOT
		0;				!SPECOP
		0;				!FIELDREF
		0;				!STORCLS
		0;				!REGCONTENTS
		0;				!LABOP
		0;				!STATEMENT
		0;				!IOLCLS
		BEGIN				!INLINFN
			IF .CNODE[ARG2PTR] NEQ 0 THEN
				ARGCMN(.CNODE[ARG2PTR],.LG)
		END
	TES);
END;

ROUTINE DELLNK(CNODE)=
BEGIN
	!REMOVE COMMON SUB-EXPRESSION CNODE FROM THE LINKED LISTOF
	!SAME HEADED BY BACKST.

	EXTERNAL BACKST;
	MAP BASE BACKST:PREV:P1:CNODE;

	!INITIALIZE THINGS
	PREV_.BACKST;
	P1_.BACKST[SRCLINK];

	!LOOK THROUGH THE LIST
	WHILE .P1 NEQ 0 DO
	BEGIN
		IF .P1 EQL .CNODE THEN
		BEGIN
			PREV[SRCLINK]_.CNODE[SRCLINK];
			RETURN;
		END;
		PREV_.P1;
		P1_.P1[SRCLINK];
	END;
END;
ROUTINE LOCLDEPD=
BEGIN
	!EXAMINE LINKED LIST OF COMMON SUB-EXPRESSIONS. BACKST
	!IS THE HEAD OF THE LIST. EXPRESSIONS ARE ORDERED
	!FROM THE BOTTOM - UP IN THE TREE SENSE.
	!THIS IS THE CONTROLLING ROUTINE FOR THE GENERAL PROCESS:
	!	DETERMINE IF EACH EXPRESSION HAS OTHER COMMON-SUBS UBDER IT
	!	IF SO, LOOK THESE UP IN THE HASH TABLE.
	!	IF THE USE COUNT OF THE SUBORDINATE = THE USECNT OF
	!	THE PARENT, THEN REMOVE THE LITTLE ONE, AND ITS
	!	COMMON SUB-EXPRESSION NODE.

	OWN PEXPRNODE EXPR;
	MAP BASE T:PAE;
	EXTERNAL BACKST;
	MAP BASE BACKST;

	EXPR_.BACKST[SRCLINK];

	!FOR EACH EXPRESSION ON THE LIST
	WHILE .EXPR NEQ 0 DO
	BEGIN
		IF .EXPR[A2VALFLG] THEN
			HASHIT(.EXPR[ARG2PTR],UNARY)
		ELSE
			HASHIT(.EXPR[ARG2PTR],STGHT);
		!LOOK UP THE EXPRESSION IN THE HASH TABLE
		PHI_TBLSRCH();
		!IF THERE ARE COMMON SUBS UNDER IT
		IF .PHI[CMNUNDER] THEN
		BEGIN
			!LOOK AT EACH ARG OF THE EXPRESSION.
			!FIRST LOOK AT REAL EXPRESSION
			PAE_.EXPR[ARG2PTR];

			!LOK1SUBS AND LOK2SUBS RETURN 1 IF WE ARE
			!INTERESTED IN THIS ONE AND THE USE COUNT IN THE
			!GLOBAL QQ

			IF LOK1SUBS(.PAE,0) THEN
				IF .QQ EQL .PHI[USECNT] THEN
				BEGIN
					T_.PAE[ARG1PTR];
					PAE[ARG1PTR]_.T[ARG2PTR];
					!RESET VALFLGS 
					PAE[A1VALFLG]_0;
					DELLNK(.T);
					!ALSO FIX PARENT
					T_.PAE[ARG1PTR];
					T[PARENT]_.PAE;
				END;
			IF LOK2SUBS(.PAE,0) THEN
				IF .QQ EQL .PHI[USECNT] THEN
				BEGIN
					T_.PAE[ARG2PTR];
					PAE[ARG2PTR]_.T[ARG2PTR];
					!RESET VALFLG
					PAE[A2VALFLG]_0;
					DELLNK(.T);
					!FIX PARENT TOO
					T_.PAE[ARG2PTR];
					T[PARENT]_.PAE;
				END;
		END;
		EXPR_.EXPR[SRCLINK];
	END;			!WHILE
	!CLEANUP THE EXPRESSION NODES THAT HAVE EXPRUSE LEFT IN THEM

	EXPR_.BACKST[SRCLINK];
	WHILE .EXPR NEQ 0 DO
	BEGIN
		EXPR[EXPRUSE]_0;
		EXPR_.EXPR[SRCLINK];
	END;
	!ALSO GO THRU THE HASH TABLE AND MARK THE ENTRIES EMPTY
	SLINGHASH();
END;
	
END
ELUDOM