Google
 

Trailing-Edge - PDP-10 Archives - FORTRAN-10_V7wLink_Feb83 - p2s1.bli
There are 26 other files named p2s1.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) DIGITAL EQUIPMENT CORPORATION 1972, 1983
!AUTHOR: S. MURPHY/JNG/DCE/TFV/CDM/RVM/AHM

MODULE P2S1(SREG=#17,VREG=#15,FREG=#16,DREGS=4,RESERVE(0,1,2,3),GLOROUTINES) =
BEGIN



GLOBAL BIND P2S1V = 7^24 + 0^18 + #1706;	! Version Date:	22-Dec-82

%(

***** Begin Revision History *****

57	-----	-----	DO NOT CHECK FOR EXPONEN INVOLVING A LOOP 
			INDEX UNTIL AFTER IN LINE EXPONENS HAVE BEEN
			DETECTED (SO THAT I**2 DOESNT CAUSE THE LP INDEX
			TO BE MATERIALIZED)
58	-----	-----	FIX TYPO IN "P2SKFN". WHEN REMOVE A NEG FROM
			UNDER AN IN-LINE FN, WHEN
			GETTING PTR TO NODE TO SET PARENT PTR, SHOULD
			LOOK AT "CNODE[ARG1PTR]", (NOT ARGNODE[ARG1PTR])
59	-----	-----	IN "ARSKOPT", USE "KEXPIX" TO FOLD EXPONEN OF CONSTS
			(RATHER THAN SQROP,CUBOP,P4OP)
60	434	19211	CHECK IF FN PARAM IS DO LOOP INDEX AFTER CONST
			FOLDING IN CASE I+0 TYPE CONSTRUCTION., (JNG)
61	445	19632	REDUCE CHANCE OF STACK OVERFLOW BY CUTTING
			DOWN NUMBER OF LOCALS FOR P2SKARITH, (DCE)
62	671	NVT	WHEN SWAPPING ARGS, SWAP DEF PTS TOO, (DCE)

***** Begin Version 6 *****

63	761	TFV	1-Mar-80	-----
	Remove KA10FLG and use /GFLOATING when rounding DP to SP

64	1031	TFV	25-Nov-80	------
	When folding relationals, chose low or high word of each constant
	based on VALTP1 since octals are not converted to real under GFLOATING

***** Begin Version 7 *****

65	1264	CDM	25-Sept-81
	Add code to P2SKFN to check if function is a type conversion NOP
	and if so to remove the node.

66	1273	CDM	15-Oct-81
	Change P2SKFN to not change functions into inline for octal 
	arguments (problem with /OPT otherwise).

67	1431	CKS	4-Dec-81
	Add P2SKSUBSTR to do skeleton optimizations for substring nodes.  Also
	add a temporary null routine to optimize concatenation.

68	1452	CKS	4-Jan-82
	Do not optimize A(1:2) to a .D variable if A is a formal variable.

1474	TFV	15-Mar-82
	Write  P2SKCONC  to  perform   the  skeleton  optimization   for
	concatenation.   It  walks  down   the  argument  list  of   the
	concatenation   performing   skeleton   optimizations   on   the
	sub-expressions.   If   all   the   lengths   are   fixed,   the
	concatenation node  is  changed  to an  OPERSP  of  CONCTF,  the
	ARG1PTR field is also filled in with a constant table entry  for
	the  length  of  the   concatenation  in  characters.   If   the
	concatenation has a  known maximum length,  the OPERSP field  is
	changed to CONCTM.   It also folds  all the concatenations  into
	one concatenation node.

1522	TFV	29-Mar-82
	Change P2SKSUBSTRING to  give the substring  bound out of  range
	error for upper bound less than lower bound, and for lower bound
	less than 1.

1535	CDM	17-May-82
	Optimize CHAR(constant) and ICHAR(constant) to be constants.

1542	RVM	25-May-82
	Convert REAL constants (stored in double precision) back to 
	single precision before folding LOGICAL expressions.  Under
	/GFLOATING, REAL numbers do not have the same bit pattern
	at compile-time that they have at execution time, so the
	conversion must be done for the results gotten at compile-
	time to agree with those gotten at run-time.

1557	CKS	14-Jun-82
	Detect substrings with constant bounds which have upper bound
	greater than string length.

1567	CDM	24-Jun-82
	Massive restructuring for inline  functions and creation of  new
	routine P2SILF.  Addition of code  to fold CHAR, ICHAR, and  LEN
	to constants.

1641	AHM	10-Oct-82
	When P2SKCONCAT  sees  the expression  A//(B//C)//D,  it  will
	change it into  A//B//C//D.  Make  it also  change the  parent
	pointers for B and C to point  to the new concat node if  they
	have parent pointers.

1655	CDM	25-Oct-82
	Allow character inline functions for arguments to concatenation.

1706	TFV	22-Dec-82
	Fix  P2SKSUBSTRING  for   substring  assignments  to   character
	function values.

***** End Revision History *****

)%


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

	!***************************************************************
	! Initial  pass  of  phase  2  skeleton.   This  pass  over   an
	! expression tree performs the following:
	!
	! 	1.  Eliminates neg and not  nodes, forcing them down  to
	! 	    the bottom if possible.  In doing this the following
	! 	    globals are used:
	!
	! 		NEGFLG - If this flag is  true when the  routine
	!			 is called for a given node, a neg is to
	!			 be forced down  from above.  This  flag
	!			 is returned  true if  the parent  above
	!			 this node must handle negation for this
	!			 node.
	!
	!		NOTFLG - Like  negflg except  indicating that  a
	!			 not is to be forced down (or back up).
	!
	!	2. Eliminates the subtract operator,  changing it to add
	!	   and propagating the neg down over the 2nd arg
	!
	!	3. Checks  for  any  operations which are  performed  on
	!	   constants and  may  be  performed  at  compile  time.
	!	   Performs such operations  and replaces their  entries
	!	   in the  expression tree  by the  resultant  constant.
	!	   Creates  constant   table  entries   for  these   new
	!	   constants.
	!
	!	4. Detects multiplication  or  division  by  a  constant
	!	   power of 2  and changes the  node to p2mul.   Detects
	!	   multiplication by a power of 2 plus 1.
	!
	!	5. Detects  exponentiation to a small  constant  integer
	!	   power.
	!
	!	6. N-ary nodes are put into canonical order.
	!
	! This   pass   is   performed   before   common   subexpression
	! elimination.  It is performed before  phase 2 when phase 2  is
	! present.  It  has a  routine  corresponding to  each  operator
	! class.  To process a given node, it dispatches to the  routine
	! corresponding to its  operator class, via  the dispatch  table
	! "P2SKL1DISP".
	!
	! These routines are called with the argument CNODE - a  pointer
	! to the node in the tree  to be processed.  They each return  a
	! pointer to  the node  to  replace CNODE  (this will  be  CNODE
	! itself unless constant elimination or neg/not propogation  has
	! been performed).
	!***************************************************************

FORWARD
	P2SKBL(1),
	BLSKOPT(1),
	P2SKIGNORE(1),
	P2SKREL(1),
	RELSKOPT(1),
	P2SKFN(1),
	P2SKARITH(1),
	ARSKOPT(1),
	P2SKLTP(1),
	P2SKLARR(1),
	P2SKNEGNOT(1),
%1431%	P2SKCONCAT(1),
%1431%	P2SKSUBSTR(1),
%1567%	P2SILF(1);

EXTERNAL
	ARCMB,
	BLCMB,
	C1H,
	C1L,
	C2H,
	C2L,
	CANONICALIZE,
	CDONODE,
	COPRIX,
%1474%	CORMAN,		! Routine to get some space from free memory
	CGERR,		! Error routine for Internal Compiler Errors
	CMBEQLARGS,
%761%	CNSTCM,
	CNTMPY,
	DOWDP,		! Global used in  determining whether a  do-loop
			! index should live in a reg
%761%	DNEGCNST,
%1522%	E165,		! Substring bound out of range error
%1567%	E202,		! CHAR library function error
%1522%	FATLERR,	! Error routine
%761%	KARIGB,
%761%	KARIIB,
%761%	KBOOLBASE,
	KDNEGB,
	KDPRL,
%1542%	KGFOCT,
	KGFRL,
	KSPECB,
	KSPECG,
%761%	KTYPCB,
%761%	KTYPCG,
	MAKEPR,
%1535%	MAKLIT,		! Makes literal constant entry
	NEGFLG,
	NEGOFNOT,
	NEWDVAR,	! Makes new .Dnnn variable
	NOTFLG,
	NOTOFNEG,
	SAVSPACE,	! Return free space
%1567%	CHEXLEN,	! Returns length of character expression or LENSTAR
	SETPIMMED,
	SETPVAL,	
	SKERR,
	TAKNEGARG,
	TAKNOTARG,
%761%	TBLSEARCH,
	USERFNFLG;	! Flag indicating that this statement had a call
			! to a user function.

	!***************************************************************
	! Define the  dispatch  table for  phase  2 skeleton  -  have  a
	! routine for each operator class
	!***************************************************************

BIND DUMDUM = UPLIT(
	P2SKL1DISP GLOBALLY NAMES
		P2SKBL,
		P2SKIGNORE,	! Should get here very rarely (valflg is
				! usually set and checked)
		P2SKREL,
		P2SKFN,
		P2SKARITH,
		P2SKLTP,
		P2SKLARR,
		P2SKIGNORE,	! Common sub expression
		P2SKNEGNOT,	! Neg/not
		P2SKIGNORE,	! Special ops (p2mul, etc.)
		P2SKIGNORE,	! Fieldref
		P2SKIGNORE,	! Storecls
		P2SKIGNORE,	! Regcontents
		P2SKIGNORE,	! Label
		P2SKIGNORE,	! Statement
		P2SKIGNORE,	! Iolscls
		P2SKIGNORE,	! In-line-fn (since  these are  inserted
				! in p2s, should not encounter them)
%1431%		P2SKSUBSTR,	! Substring
%1431%		P2SKCONCAT);	! Concatenation

GLOBAL ROUTINE P2SKBL(CNODE)=
BEGIN
	!***************************************************************
	! Initial pass of phase 2 skeleton for a boolean
	!***************************************************************

	MAP    PEXPRNODE CNODE;

	LOCAL
		PEXPRNODE ARG1NODE,
		PEXPRNODE ARG2NODE,
		PRVNEGFLG,
		ARGNOTFLG;

	DEBGNODETST(CNODE);		! For debugging only
	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];

	!***************************************************************
	! For neg/not elimination. Cannot force  a neg down across  this
	! node.  Force down a not by:
	!	not(a and b)=(not a) or (not b)
	!	not(a or b)=(not a) and (not b)
	!	not(a xor b)=a eqv b
	!	not(a eqv b)=a xor b
	!***************************************************************

	PRVNEGFLG = .NEGFLG;
	ARGNOTFLG = .NOTFLG;

	IF.NOTFLG
	THEN
	BEGIN
		! Set opersp to OR from AND, AND from OR, EQV from  XOR,
		! XOR from eqv

		CNODE[BOPRFLG] = NOT.CNODE[BOPRFLG];

		IF .CNODE[BOOLCLS] NEQ ANDORCLS
		THEN ARGNOTFLG = FALSE;
	END;


	! Process 1st arg

	! If arg is a leaf, do not walk down there

	IF .CNODE[A1VALFLG]
	THEN
	BEGIN
		IF .ARGNOTFLG THEN CNODE[A1NOTFLG] = 1;
	END
	ELSE
	BEGIN
		NEGFLG = FALSE;
		NOTFLG = .ARGNOTFLG;
		ARG1NODE = (.P2SKL1DISP[.ARG1NODE[OPRCLS]])(.ARG1NODE);

		! If neg or  not was  propagated up from  arg1, set  the
		! flags in CNODE

		CNODE[A1NEGFLG] = .NEGFLG<0,1>;
		CNODE[A1NOTFLG] = .NOTFLG<0,1>;
	END;

	! If arg1 is a constant (or  was collapsed into into a  constant
	! by the walk over  it) and a1notflg is  set, perform the  'not'
	! operation

	IF .ARG1NODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		IF .CNODE[A1NOTFLG]
		THEN
		BEGIN
			ARG1NODE = NOTCNST(ARG1NODE);
			CNODE[A1NOTFLG] = 0;
		END
	END;

	CNODE[ARG1PTR]_.ARG1NODE;

	! Process 2nd arg

	! If arg is a leaf, do not walk down there

	IF .CNODE[A2VALFLG]
	THEN
	BEGIN
		IF .ARGNOTFLG THEN CNODE[A2NOTFLG] = 1;
	END
	ELSE
	BEGIN	! For arg2 not a leaf (or common subexpr)

		NEGFLG = FALSE;
		NOTFLG = .ARGNOTFLG;
		ARG2NODE = (.P2SKL1DISP[.ARG2NODE[OPRCLS]])(.ARG2NODE);
		CNODE[A2NEGFLG] = .NEGFLG<0,1>;
		CNODE[A2NOTFLG] = .NOTFLG<0,1>;
	END;

	! If arg2 is a constant (or was collapsed into one), perform the
	! 'not' operation on it if necessary

	IF .ARG2NODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		IF .CNODE[A2NOTFLG]
		THEN
		BEGIN
			ARG2NODE = NOTCNST(ARG2NODE);
			CNODE[A2NOTFLG] = 0;
		END;
	END;

	CNODE[ARG2PTR] = .ARG2NODE;
	NEGFLG = .PRVNEGFLG;
	NOTFLG = FALSE;

	! Check  for  operations  on  constants  and  operations  on   2
	! identical args, fold if can

	RETURN BLSKOPT(.CNODE);

END;	! of P2SKBL



GLOBAL ROUTINE BLSKOPT(CNODE)=
BEGIN
	!***************************************************************
	! Routine to  check whether  a boolean  operation has  arguments
	! which are either constant or identical to each other and hence
	! can be folded.  CNODE is a  pointer to the boolean node to  be
	! examined.  If  CNODE can  be folded,  this routine  returns  a
	! pointer to the node which will replace CNODE in the expression
	! tree.  Otherwise it returns a pointer to cnode.
	!***************************************************************

	REGISTER
		PEXPRNODE ARG1NODE,
		PEXPRNODE ARG2NODE,
%1542%		C1,
%1542%		C2;

	MAP PEXPRNODE CNODE;

	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];

	! Check for arg1 and arg2 both  constants and if so compute  the
	! value corresponding to CNODE and  replace CNODE by a  constant
	! table entry for that value.

	IF .ARG1NODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		IF.ARG2NODE[OPR1] EQL CONSTFL
		THEN
		BEGIN
			! Globals used by the assembly language  routine
			! that performs the operations are COPRIX,  C1L,
			! C2L.  Set C1L and C2L  to the single words  to
			! be operated on

%1542%			C1 = IF .ARG1NODE[VALTYPE] EQL REAL AND .GFLOAT
%1542%			     THEN
%1542%			     BEGIN
%1542%					C1H = .ARG1NODE[CONST1];
%1542%					C1L = .ARG1NODE[CONST2];
%1542%					COPRIX = KGFOCT;
%1542%					CNSTCM();
%1542%					.C2L
%1542%			      END
			      ELSE IF .ARG1NODE[VALTP1] EQL INTEG1
				   THEN .ARG1NODE[CONST2]
				   ELSE .ARG1NODE[CONST1];

%1542%			C2 = IF .ARG2NODE[VALTYPE] EQL REAL AND .GFLOAT
%1542%			     THEN
%1542%			     BEGIN
%1542%					C1H = .ARG2NODE[CONST1];
%1542%					C1L = .ARG2NODE[CONST2];
%1542%					COPRIX = KGFOCT;
%1542%					CNSTCM();
%1542%					.C2L
%1542%			      END
			      ELSE IF .ARG2NODE[VALTP1] EQL INTEG1
				   THEN .ARG2NODE[CONST2]
				   ELSE .ARG2NODE[CONST1];

%1542%			C1L = .C1;
%1542%			C2L = .C2;

			COPRIX = .CNODE[OPERSP] + KBOOLBASE;

			! Find the result of  this operation on these  2
			! constants

			CNSTCM();

			! Set valflg in parent of CNODE

			SETPVAL(.CNODE);

			! Replace CNODE by a new constant node

			CNODE = MAKECNST(LOGICAL,0,.C2L);
		END
		ELSE
			!**************************************
			! Check for:
			! 	A AND TRUE = A
			! 	A AND FALSE = FALSE
			! 	A OR  TRUE = TRUE
			! 	A OR  FALSE = A
			! 	A EQV TRUE = A
			! 	A XOR TRUE = NOT A
			! 	A EQV FALSE = NOT A
			! 	A XOR FALSE = A
			! and do the replacement
			!**************************************

			CNODE = BLCMB(.CNODE,.ARG1NODE,.ARG2NODE);
	END
	ELSE		! Do the same replacement for arg2

	IF .ARG2NODE[OPR1] EQL CONSTFL
	THEN CNODE = BLCMB(.CNODE,.ARG2NODE,.ARG1NODE)
	ELSE
		!**************************************
		! Check for:
		! 	A AND A =A
		! 	A AND (NOT A) = FALSE
		! 	A OR A = A
		! 	A OR (NOT A) = TRUE
		! 	A EQV A = TRUE
		! 	A EQV (NOT A) = FALSE
		! 	A XOR A = FALSE
		! 	A XOR (NOT A) = TRUE
		! and do the replacement
		!**************************************

	IF .CNODE[ARG1PTR] EQL .CNODE[ARG2PTR]
	THEN CNODE = CMBEQLARGS(.CNODE,FALSE);

	RETURN CANONICALIZE(.CNODE);

END;	! of BLSKOPT


GLOBAL ROUTINE P2SKIGNORE(CNODE)=
BEGIN
	!***************************************************************
	! Phase  2  skeleton  routine  for  a  data  item  (constant  or
	! variable).  This routine is  also used for regcontents  nodes,
	! labels, etc.  In  general, do  not walk  down to  a data  node
	! because the valflg in the parent is set, and always check  the
	! flag before walking down to a  son.  This is here to keep  the
	! compiler from dying in those  rare cases where the valflg  was
	! left unset (it is used for elements on iolists where there  is
	! no valflg).
	!***************************************************************

	RETURN .CNODE

END;	! of P2SKIGNORE



GLOBAL ROUTINE P2SKREL(CNODE)=
BEGIN
	!***************************************************************
	! Initial pass of phase 2 skeleton for a relational
	!***************************************************************

	MAP PEXPRNODE CNODE;

	LOCAL
		PEXPRNODE ARG1NODE,
		PEXPRNODE ARG2NODE,
		PRVNEGFLG;

	DEBGNODETST(CNODE);		! For debugging only

	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];

	! For neg/not elimination - can force down a not by changing the
	! sense of the relational.  Cannot force down a neg.

	IF .NOTFLG THEN CNODE[OPERSP] = CMREL(.CNODE[OPERSP]);

	PRVNEGFLG = .NEGFLG;

	! Process first argument.  Do  not walk down to  arg if it is  a
	! leaf or common subexpr.

	IF NOT .CNODE[A1VALFLG]
	THEN
	BEGIN
		NEGFLG = FALSE;
		NOTFLG = FALSE;
		CNODE[ARG1PTR] = (.P2SKL1DISP[.ARG1NODE[OPRCLS]])(.ARG1NODE);
		CNODE[A1NEGFLG] = .NEGFLG<0,1>;
		CNODE[A1NOTFLG] = .NOTFLG<0,1>;
	END;

	! Process second argument.  Do not walk  down to arg if it is  a
	! leaf or common subexpr.

	IF NOT .CNODE[A2VALFLG]
	THEN
	BEGIN
		NEGFLG = FALSE;
		NOTFLG = FALSE;
		CNODE[ARG2PTR] = (.P2SKL1DISP[.ARG2NODE[OPRCLS]])(.ARG2NODE);
		CNODE[A2NEGFLG] = .NEGFLG<0,1>;
		CNODE[A2NOTFLG] = .NOTFLG<0,1>;
	END;

	! Set negflg and notflg  to the values to  be passed back up  to
	! parent

	NOTFLG = FALSE;
	NEGFLG = .PRVNEGFLG;

	! Check for operations on constants and operations on  identical
	! args that can be folded

	RETURN RELSKOPT(.CNODE);

END;	! of P2SKREL


GLOBAL ROUTINE RELSKOPT(CNODE)=
BEGIN
	!***************************************************************
	! Routine to  check a  relational node  for arguments  equal  to
	! constants, or to eachother, and to  fold such a node if it  is
	! possible  to  do  so.   The  argument  CNODE  points  to   the
	! relational node to  be examined.   If the node  can be  folded
	! then a pointer to the  new node to replace  it in the tree  is
	! returned.  Otherwise a pointer to CNODE is returned.
	!***************************************************************

	OWN
		PEXPRNODE ARG1NODE,
		PEXPRNODE ARG2NODE;

	MAP PEXPRNODE CNODE;

	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];

	!***************************************************************
	! If arg1 is equal to arg2 -
	!	substitute TRUE for a eq a, a le a, a ge a
	!	substitute FALSE for a lt a, a gt a, a ne a
	!***************************************************************

	IF .CNODE[ARG1PTR] EQL .CNODE[ARG2PTR]
	THEN RETURN CMBEQLARGS(.CNODE,FALSE);


	!***************************************************************
	! Check for both args negated.
	! Transform:
	!	-a lt  -b = a gt b
	!	-a leq -b = a geq b
	!	-a eq  -b = a eq b
	!	-a gt  -b = a lt b
	!	-a geq -b = a leq b
	!	-a neq -b = a neq b
	!***************************************************************

	IF .CNODE[A1NEGFLG] AND .CNODE[A2NEGFLG]
	THEN
	BEGIN
		CNODE[A1NEGFLG] = 0;
		CNODE[A2NEGFLG] = 0;

		IF NOT EQREL(.CNODE[OPERSP])
		THEN CNODE[OPERSP] = REVREL(.CNODE[OPERSP]);
	END;

	! If the operands  are both constants,  evaluate the  relational
	! and replace it in the tree by either TRUE or FALSE.  If one of
	! the arguments  is a  constant, let  that argument  be the  2nd
	! argument.

	IF .ARG1NODE[OPR1] EQL CONSTFL
		AND .ARG1NODE[VALTYPE] NEQ DOUBLOCT
	THEN
	BEGIN
		!!!!!!?????????!!!!!!!!
		%(****FEB 23,1972 - THE FOLLOWING BLOCK WAS INSERTED TO
			PREVENT A BLISS BUG THAT DELETED CODE . THIS BLOCK FORCES
			BLISS TO USE 2 TEMP REGS***)%
		BEGIN
			OWN T,T1,T2,T3;
			T = 1; T1 = 2; T2 = 3; T3 = 4;
		END;

		IF .ARG2NODE[OPR1] EQL CONSTFL
			AND .ARG2NODE[VALTYPE] NEQ DOUBLOCT
		THEN
		BEGIN
			OWN
				KN,
				K1H,	! Hi word of const1 after the round
				K1L,	! Low word of const1 after the round
				K2H,	! Hi word of const2 after the round
				K2L;	! Low word of const2 after the round

			! For  real  variables   and  double   precision
			! variables, must round before compare

%761%			IF .ARG1NODE[VALTYPE] EQL REAL
			THEN
			BEGIN
				! Set up the globals for constant folding

				C1H = .ARG1NODE[CONST1];
				C1L = .ARG1NODE[CONST2];

				! To round double precision to real

%761%				IF .GFLOAT
%761%					THEN COPRIX = KGFRL
%761%					ELSE COPRIX = KDPRL;

				! Do the rounding, leave result in  C2H,
				! C2L

				CNSTCM();
				K1H = .C2H;
				K1L = .C2L
			END
			ELSE
			BEGIN
				! If rounding is not needed

				K1H = .ARG1NODE[CONST1];
				K1L = .ARG1NODE[CONST2];
			END;

%761%			IF .ARG2NODE[VALTYPE] EQL REAL
			THEN
			BEGIN
				! Set up the globals for constant folding

				C1H = .ARG2NODE[CONST1];
				C1L = .ARG2NODE[CONST2];

				! To round double precision to real

%761%				IF .GFLOAT
%761%					THEN COPRIX = KGFRL
%761%					ELSE COPRIX = KDPRL;

				! Do the rounding, leave result in  C2H,
				! C2L

				CNSTCM();
				K2H = .C2H;
				K2L = .C2L
			END
			ELSE
			BEGIN
				! If rounding is not needed

				K2H = .ARG2NODE[CONST1];
				K2L = .ARG2NODE[CONST2];
			END;

			KN = 
			BEGIN
				IF .ARG1NODE[DBLFLG]
				THEN
				%(***IF MUST COMPARE 2-WD VAL****)%
				BEGIN
					CASE .CNODE[OPERSP] OF SET
					%(***UNUSED OPERSP CODE - SHOULD NEVER GET HERE***)%
					BEGIN
						SKERR();
						FALSE
					END;
					%(** LT **)%
					(.K1H LSS .K2H)
						OR (.K1H EQL .K2H AND .K1L LSS .K2L);
					%(** EQ **)%
					(.K1H EQL .K2H) AND (.K1L EQL .K2L);
					%(** LE **)%
					(.K1H LSS .K2H)
						OR (.K1H EQL .K2H AND .K1L LEQ .K2L);
					%(**UNUSED CODE SHOULD NEVER GET HERE**)%
					BEGIN
						SKERR();
						FALSE
					END;
					%(** GE **)%
					(.K1H GTR .K2H)
						OR (.K1H EQL .K2H AND .K1L GEQ .K2L);
					%(** NE**)%
					(.K1H NEQ .K2H) OR (.K1L NEQ .K2L);
					%(** GT **)%
					(.K1H GTR .K2H)
						OR (.K1H EQL .K2H AND .K1L GTR .K2L);
					TES
				END

				ELSE
				%(***IF MUST COMPARE SINGLE-WD VALS***)%
				BEGIN
					OWN C1,C2;
					%(***SET C1 AND C2 TO THE VALS TO BE COMPARED***)%
![1031] Use low or high word of each constant based on VALTP1
![1031] since octals are not converted to reals under GFLOATING
%[1031]%       				IF .ARG1NODE[VALTP1] EQL INTEG1
%[1031]%	       			THEN	C1 = .K1L
%[1031]%				ELSE	C1 = .K1H;

%[1031]%       				IF .ARG2NODE[VALTP1] EQL INTEG1
%[1031]%	       			THEN	C2 = .K2L
%[1031]%				ELSE	C2 = .K2H;

					CASE .CNODE[OPERSP] OF SET
					%(***UNUSED OPERSP CODE - SHOULD BEVER GET HERE***)%
					BEGIN
						SKERR();
						FALSE
					END;
					%(***LT****)%
					.C1 LSS .C2;
					%(***EQ****)%
					.C1 EQL .C2;
					%(***LE****)%
					.C1 LEQ .C2;
					%(***UNUSED OPERSP CODE - SHOULD NEVER GET HERE***)%
					BEGIN
						SKERR();
						FALSE
					END;
					%(***GE***)%
					.C1 GEQ .C2;
					%(***NE***)%
					.C1 NEQ .C2;
					%(***GT***)%
					.C1 GTR .C2
					TES
				END
			END;
			%(***SET THE VALFLG IN THE PARENT OF CNODE***)%
			SETPVAL(.CNODE);


			%(***RETURN THE CONSTANT TABLE ENTRY FOR THE VAL OF THIS RELATIONAL***)%
			RETURN MAKECNST(LOGICAL,0,
				BEGIN
					IF .KN THEN TRUE ELSE FALSE
				END);
		END


		%(***IF ARG1 IS A CONSTANT AND ARG2 IS NOT; SWAP THE 2
			ARGS ***)%
		ELSE
		BEGIN
			IF NOT EQREL(.CNODE[OPERSP])
			THEN
			CNODE[OPERSP] = REVREL(.CNODE[OPERSP]);
			SWAPARGS(CNODE);
![671] WHEN WE SWAP THE ARGUMENTS, BE SURE TO SWAP THE DEF PTS TOO
%[671]%			IF .FLGREG<OPTIMIZE> THEN
%[671]%			BEGIN
%[671]%				ARG1NODE = .CNODE[DEFPT2];
%[671]%				CNODE[DEFPT2] = .CNODE[DEFPT1];
%[671]%				CNODE[DEFPT1] = .ARG1NODE
%[671]%			END;
			ARG1NODE = .CNODE[ARG1PTR];
			ARG2NODE = .CNODE[ARG2PTR];
		END;
	END;





	%(*****IF ONE OF THE ARGS IS ZERO AND THE OTHER IS A SUM, TRANSFORM:
		(A+B).REL.0=A.REL.-B
	*********)%

	IF ( NOT .CNODE[A1VALFLG]) AND (.ARG2NODE[OPR1] EQL CONSTFL)
	THEN
	BEGIN
		IF (.ARG2NODE[CONST1] EQL 0) AND (.ARG2NODE[CONST2] EQL 0) AND (.ARG1NODE[OPR1] EQL ADDOPF)

			AND NOT .CNODE[A1NOTFLG]
		THEN
		BEGIN

			%(****MAKE ARG1 UNDER CNODE BE ARG1 UNDER THE SUM, MAKE ARG2 BE
				ARG2 UNDER THE SUM WITH THE SIGN REVERSED****)%
			CNODE[ARG1PTR] = .ARG1NODE[ARG1PTR];
			CNODE[A1FLGS] = .ARG1NODE[A1FLGS];
			CNODE[ARG2PTR] = .ARG1NODE[ARG2PTR];
			CNODE[A2FLGS] = .ARG1NODE[A2FLGS];
			CNODE[A2NEGFLG] = NOT .CNODE[A2NEGFLG];
			%(***CORRECT PARENT PTRS IN THE 2 SUBNODES WHICH WERE MOVED***)%
			ARG1NODE = .CNODE[ARG1PTR];
			ARG2NODE = .CNODE[ARG2PTR];
			IF .ARG1NODE[OPRCLS] EQL DATAOPR
			THEN
			CNODE[A1VALFLG] = 1
			ELSE
			ARG1NODE[PARENT] = .CNODE;

			IF .ARG2NODE[OPRCLS] EQL DATAOPR
			THEN
			CNODE[A2VALFLG] = 1
			ELSE
			ARG2NODE[PARENT] = .CNODE;
		END;
	END;

	RETURN .CNODE;

END;	! of RELSKOPT


GLOBAL ROUTINE P2SKFN(CNODE)=
%(*************************************************************************
	Initial pass of phase 2 skeleton for a function call.  Cannot force
	neg or not down across a fn call.
*************************************************************************)%
BEGIN

	MAP OBJECTCODE DOWDP;
	MAP PEXPRNODE CDONODE;

	MAP OBJECTCODE USERFNFLG;
	MAP PEXPRNODE CNODE;

%1567%	REGISTER
		ARGUMENTLIST ARGLST,	! Argument list to function
		PEXPRNODE FNNAMENTRY;	! Function symble table node

	LOCAL
		PEXPRNODE ARGNODE,	!Argument node for spec arg
		PRVNEGFLG,
		PRVNOTFLG;


	DEBGNODETST(CNODE);		!FOR DEBUGGING ONLY


	FNNAMENTRY = .CNODE[ARG1PTR];
	ARGLST = .CNODE[ARG2PTR];

	! If this fn is not a  library fn, set a global indicating  that
	! this stmnt includes a call to a user fn

	IF .CNODE[OPERSP] NEQ LIBARY  THEN  USERFNFLG = TRUE;


	%(***IF THIS FN IS A STMNT FN AND THIS REFERENCE IS INSIDE A DO LOOP
		THEN THE INDEX OF THAT LOOP MUST BE MATERIALIZED (SINCE THE
		STMNT FN CAN REFERENCE THE VAR)***)%
	IF .FNNAMENTRY[IDATTRIBUT(SFN)] THEN DOWDP[DOMTRLZIX] = 1;

	%(***PERFORM PHASE 2 SKEL OPTIMS ON ALL ARGS***)%
	IF .CNODE[ARG2PTR] NEQ 0
	THEN
	BEGIN
		PRVNEGFLG = .NEGFLG;
		PRVNOTFLG = .NOTFLG;

		%(*** PROCESS ALL ARGUMENTS ***)%
		INCR CT FROM 1 TO .ARGLST[ARGCOUNT]
		DO
		BEGIN
			ARGNODE = .ARGLST[.CT,ARGNPTR];
			IF NOT .ARGLST[.CT,AVALFLG]
			THEN
			%(***UNLESS THIS ARG IS A LEAF OR A COMMON SUBEXPR, PROCESS IT***)%
			BEGIN
				NEGFLG = FALSE;
				NOTFLG = FALSE;
				ARGLST[.CT,ARGNPTR] = (.P2SKL1DISP[.ARGNODE[OPRCLS]])(.ARGNODE);
			END;
			%(***CHECK WHETHER THIS ARG IS THE INDEX OF A DO LOOP THAT
				INCLUDES THIS STMNT. IF SO, WILL NOT BE ABLE TO
				HAVE THAT LOOP INDEX LIVE IN A REGISTER***)%
			IF .ARGLST[.CT,ARGNPTR] EQL .DOWDP[DOINDUC]
			THEN  DOWDP[DOMTRLZIX] = 1;

		END;

		%(***RESTORE NEGFLG AND NOTFLG TO THE VALS THAT THEY  HAD WHEN ENTERED***)%
		NEGFLG = .PRVNEGFLG;
		NOTFLG = .PRVNOTFLG;
	END;


	! Check for whether this fn should  be expanded in line. If  so,
	! transform this  FNCALL node  into an  "in-line-fn" node  or  a
	! type-conversion node. Function won't be made inline if it  has
	! octal arguments.

	IF .FNNAMENTRY[IDINLINFLG]
%1567%	THEN	RETURN P2SILF(.CNODE);


	RETURN .CNODE;

END;	! of P2SKFN


GLOBAL ROUTINE P2SKARITH(CNODE)=
%(***
	INITIAL PASS OF PHASE 2 SKELETON FOR AN ARITHMETIC NODE
***)%
BEGIN

	MAP OBJECTCODE DOWDP;
	LOCAL PEXPRNODE ARG1NODE;
	LOCAL PEXPRNODE ARG2NODE;
	LOCAL V;
	MAP PEXPRNODE CNODE;
	! MAKE 4 BOOLEAN LOCALS LIVE INSIDE V
	!SO THAT RECURSIVE CALLS ARE LESS LIKELY TO
	!OVERFLOW OUR STACK!  THE BOOLEANS ARE DEFINED BELOW
	MACRO	PARNEG=35,1$,
		PARNOT=34,1$,
		ARG1NEG=33,1$,
		ARG2NEG=32,1$;

	DEBGNODETST(CNODE);		!FOR DEBUGGING ONLY


	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];

	%(***FORCE DOWN A NEGATIVE BY:
		-(A+B)=-A-B
		-(A-B)=-A+B
		-(A*B)=(-A)*B
		-(A/B)=(-A)/B
	***)%
	IF .NEGFLG
	THEN
	BEGIN
		CASE .CNODE[OPERSP] OF SET
		%(*** FOR ADD ***)%
		BEGIN
			V<ARG1NEG> = TRUE;
			V<ARG2NEG> = TRUE;
			V<PARNEG> = FALSE;
		END;
		%(*** FOR SUB ***)%
		BEGIN
			CNODE[OPERSP] = ADDOP;
			V<ARG1NEG> = TRUE;
			V<ARG2NEG> = FALSE;
			V<PARNEG> = FALSE;
		END;
		%(*** FOR MUL ***)%
		BEGIN
			V<ARG1NEG> = TRUE;
			V<ARG2NEG> = FALSE;
			V<PARNEG> = FALSE;
		END;
		%(*** FOR DIV ***)%
		BEGIN
			V<ARG1NEG> = TRUE;
			V<ARG2NEG> = FALSE;
			V<PARNEG> = FALSE;
		END;
		%(*** FOR EXPONENTIATION ***)%
		%(*** CANNOT FORCE NEG DOWN ***)%
		BEGIN
			V<ARG1NEG> = FALSE;
			V<ARG2NEG> = FALSE;
			V<PARNEG> = TRUE;
		END
		TES
	END
	ELSE
	BEGIN
		V<ARG1NEG> = FALSE;
		V<PARNEG> = FALSE;
		IF .CNODE[OPERSP] EQL SUBOP
		THEN
		BEGIN
			CNODE[OPERSP] = ADDOP;
			V<ARG2NEG> = TRUE;
		END
		ELSE
		V<ARG2NEG> = FALSE;
	END;

	%(*** CANNOT FORCE DOWN A NOT ***)%
	V<PARNOT> = .NOTFLG;


	%(********* PROCESS FIRST ARG **********)%

	%(****DO NOT WALK DOWN TO A NODE WHICH IS A LEAF OR COMMON SUBEXPR***)%
	IF .CNODE[A1VALFLG]
	THEN
	BEGIN
		IF .V<ARG1NEG>
		THEN CNODE[A1NEGFLG] = 1;
	END
	ELSE
	%(***IF ARG IS NOT A LEAF OR COMMON SUBEXPR***)%
	BEGIN
		NOTFLG = FALSE;
		NEGFLG =  IF .V<ARG1NEG> THEN TRUE ELSE FALSE;
		ARG1NODE = (.P2SKL1DISP[.ARG1NODE[OPRCLS]])(.ARG1NODE);
		CNODE[A1NEGFLG] = .NEGFLG<0,1>;
		CNODE[A1NOTFLG] = .NOTFLG<0,1>;
	END;

	%(***IF ARG IS A CONSTANT (OR WAS COLLAPSED INTO ONE), PERFORM NEG
		ON IT AT COMPILE TIME*****)%
	IF .ARG1NODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		IF .CNODE[A1NEGFLG]
		THEN
		BEGIN
			ARG1NODE = NEGCNST(ARG1NODE);
			CNODE[A1NEGFLG] = 0;
		END;
	END;

	CNODE[ARG1PTR] = .ARG1NODE;



	%(********* PROCESS SECOND ARG ********)%

	IF .CNODE[A2VALFLG]
	THEN
	BEGIN
		IF .V<ARG2NEG>
		THEN
		CNODE[A2NEGFLG] = 1;
	END
	ELSE
	BEGIN
		NEGFLG =  IF .V<ARG2NEG> THEN TRUE ELSE FALSE;
		NOTFLG = FALSE;
		ARG2NODE = (.P2SKL1DISP[.ARG2NODE[OPRCLS]])(.ARG2NODE);
		CNODE[A2NEGFLG] = .NEGFLG<0,1>;
		CNODE[A2NOTFLG] = .NOTFLG<0,1>;
	END;

	%(***IF ARG IS A CONSTANT (OR WAS COLLAPSED INTO ONE), PERFORM NEG
		ON IT AT COMPILE TIME*****)%
	IF .ARG2NODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		IF .CNODE[A2NEGFLG]
		THEN
		BEGIN
			ARG2NODE = NEGCNST(ARG2NODE);
			CNODE[A2NEGFLG] = 0;
		END;
	END;

	CNODE[ARG2PTR] = .ARG2NODE;


	%(*** CHECK FOR
		(-A)*(-B)=A*B
		(-A)/(-B)=A/B
	***)%
	IF .CNODE[A1NEGFLG] AND .CNODE[A2NEGFLG]
	THEN
	BEGIN
		IF .CNODE[OPERSP] EQL MULOP
		OR .CNODE[OPERSP] EQL DIVOP
		THEN
		BEGIN
			CNODE[A1NEGFLG] = 0;
			CNODE[A2NEGFLG] = 0;
		END;
	END;




	NEGFLG =  IF .V<PARNEG> THEN TRUE ELSE FALSE;
	NOTFLG =  IF .V<PARNOT> THEN TRUE ELSE FALSE;

	%(****CHECK FOR CONSTANT OPERATIONS AND OPERATIONS ON IDEXTICAL ARGS THAT CAN BE FOLDED***)%
	V =  ARSKOPT(.CNODE);

	%(***IF EITHER ARG OF AN EXPONENTIATION IS THE INDEX OF A DO LOOP THAT
		INCLUDES THAT EXPONENTIATION, CANNOT HAVE THAT LOOP INDEX LIVE IN A REG***)%
	IF .CNODE[OPR1] EQL EXPONOPF
	THEN
	BEGIN
		IF .CNODE[ARG1PTR] EQL .DOWDP[DOINDUC] OR
			(.CNODE[ARG2PTR] EQL .DOWDP[DOINDUC])
		THEN
		DOWDP[DOISUBS] = 0
	END;

	RETURN .V;

END;	! of P2SKARITH

GLOBAL ROUTINE ARSKOPT(CNODE)=
%(***************************************************************************
	FOR AN ARITHMETIC NODE, CHECK FOR OPERATIONS ON CONSTANTS AND ON IDENTICAL ARGS THAT CAN BE FOLDED.
	CALLED WITH THE ARG CNODE POINTING TO AN ARITHMETIC EXPRESSION NODE.
***************************************************************************)%
BEGIN
	MAP PEXPRNODE CNODE;
	OWN PEXPRNODE ARG1NODE:ARG2NODE;
	LABEL FOLDCNST;

	ARG1NODE = .CNODE[ARG1PTR];
	ARG2NODE = .CNODE[ARG2PTR];



	%(***
	CHECK FOR BOTH OPERANDS CONSTANTS.  IF SO, PERFORM THE
	OPERATION AT COMPILE TIME - CREATE A CONSTANT TABLE ENTRY
	FOR THE NEW CONSTANT WHICH IS THE RESULTS
	***)%
	IF .ARG1NODE[OPR1] EQL CONSTFL AND .ARG2NODE[OPR1] EQL CONSTFL
		%(***DO NOT FOLD OPERATIONS INVOLVING DOUBLE OCTALS SINCE HAVE COMPLICATIONS
			DUE TO KEEPING ALL DOUBLE-PRECISION IN KI10 FORMAT UNTIL THE END***)%
		AND .ARG1NODE[VALTYPE] NEQ DOUBLOCT AND .ARG2NODE[VALTYPE] NEQ DOUBLOCT
	THEN
FOLDCNST:	BEGIN
		%(***DO NOT FOLD COMPLEX MULTIPLY AND DIVIDE,*****)%
		IF .CNODE[VALTYPE] EQL COMPLEX AND MULORDIV(CNODE)
		THEN
		LEAVE FOLDCNST;


		%(***GLOBALS USED BY THE ASSEMBLY LANGUAGE ROUTINE THAT
			PERFORMS THE OPERATIONS ARE
			 COPRIX, C1H, C1L, C2H, C2L***)%

		%(***FOLD CONSTANTS RAISED TO INTEGER POWERS ONLY IF THEY USE 8 OR LESS MULTIPLIES***)%
		IF .CNODE[OPERSP] EQL EXPONOP
		THEN
		BEGIN
			%(***DO NOT FOLD DOUBLE-PREC EXPONENTIATION AT COMPILE TIME***)%
			IF .CNODE[DBLFLG]
			THEN LEAVE FOLDCNST
			ELSE
			BEGIN
				IF .ARG2NODE[VALTP1] EQL INTEG1
					AND CNTMPY(.ARG2NODE[CONST2]) LEQ 8	!LESS THAN 8 MULTIPLIES
				THEN
				COPRIX = KEXPIX(.CNODE[VALTP1])
				ELSE LEAVE FOLDCNST
			END
		END
		ELSE
		COPRIX = KARITHOPIX(CNODE);

		%(***PICK UP ARG1 AND ARG2. WHEN HAVE PROPAGATED CONSTANTS, WILL HAVE TO
			WORRY ABOUT NEGFLGS***)%
		C1H = .ARG1NODE[CONST1];
		C1L = .ARG1NODE[CONST2];
		C2H = .ARG2NODE[CONST1];
		C2L = .ARG2NODE[CONST2];

		%(***COMBINE THE CONSTANTS LEAVING THE RESULTS IN C2H AND C2L***)%
		CNSTCM();

		%(***SET THE VALFLG IN THE PARENT OF CNODE****)%
		SETPVAL(.CNODE);


		CNODE = MAKECNST(.CNODE[VALTYPE], .C2H, .C2L);
	END;


	IF .CNODE[OPRCLS] NEQ DATAOPR	!IF DID NOT SUCCEED IN FOLDING THIS NODE ALREADY
	THEN
	BEGIN

		%(****
			CHECK FOR ONE OF THE ARGUMENTS A CONSTANT
			IF SO, GO ATTEMPT TO MAKE THE
			VARIOUS OPTOMIZATIONS THAT CAN BE MADE ON OPS BETWEEN
			A VARIABLE(OR EXPRESSION) AND A CONSTANT.
			THESE INCLUDE RECOGNIZING CONSTANTS  AS BEING
				1. ZERO
				2. ONE
				3. MINUS ONE
				4. POWERS OF 2
				5. POWER OF 2 PLUS ONE
		*******)%

		IF .ARG1NODE[OPR1] EQL CONSTFL
		THEN
		CNODE = ARCMB(.CNODE,.ARG1NODE,.ARG2NODE,TRUE)
		ELSE
		IF .ARG2NODE[OPR1] EQL CONSTFL
		THEN
		CNODE = ARCMB(.CNODE,.ARG2NODE,.ARG1NODE,FALSE)



		%(********
			CHECK FOR:
				A+A=2*A
				A-A=0
				A/A=1
				A/-A=-1
		***********)%

		ELSE
		IF (.CNODE[ARG1PTR] EQL .CNODE[ARG2PTR])
		THEN
		CNODE = CMBEQLARGS(.CNODE,FALSE);
	END;

	%(****CANONICALIZE CNODE AND RETURN THE RESULT*****)%
	RETURN CANONICALIZE(.CNODE);

END;	! of ARSKOPT


GLOBAL ROUTINE P2SKLTP(CNODE)=
%(********
	INITIAL PASS OF PHASE 2 SKELETON FOR A TYPE-CONVERSION
	NODE.
********)%
BEGIN
	LOCAL PEXPRNODE ARGNODE;
	LOCAL SAVENOTFLG;


	MAP PEXPRNODE CNODE;

	DEBGNODETST(CNODE);		!FOR DEBUGGING ONLY


	ARGNODE = .CNODE[ARG2PTR];

	IF NOT .CNODE[A2VALFLG]
	THEN
	%(**PROCESS THE ARGUMENT UNDER THIS NODE.
		SIMPLY PASS NEG ON DOWN.
	**)%
	BEGIN
		IF NOT NOCNV(CNODE)	!IF THIS IS A TYPE-CNV THAT DOES GENERATE CODE
		THEN
		BEGIN
			SAVENOTFLG = .NOTFLG;	!CANNOT PASS A "NOT" DOWN OVER A TYPE CNV
			NOTFLG = FALSE;
		END;

		ARGNODE = (.P2SKL1DISP[.ARGNODE[OPRCLS]])(.ARGNODE);	!PROCESS ARG UNDER TPCNV



		%(***EXCEPT FOR DUMMY TYPE CONVERSION NODES, CANNOT PASS "NOT"
			UP THROUGH THE TYPE CONVERSION***)%
		IF NOT NOCNV(CNODE)
		THEN
		BEGIN
			CNODE[A2NOTFLG] = .NOTFLG<0,1>;
			NOTFLG = .SAVENOTFLG;
		END;

		%(***IF HAVE A NEG PASSED UP TO THIS NODE, MUST CHECK WHETHER IT CAN
			BE PASSED UP TO THE PARENT OF THIS NODE***)%
		IF .NEGFLG AND NOT TAKNEGARG(.CNODE[PARENT])
		THEN
		%(***IF CANNOT PASS THE NEG BACK UP, PUT IT INTO THE TPCNV NODE***)%
		BEGIN
			CNODE[A2NEGFLG] = 1;
			NEGFLG = FALSE;
		END;
	END;


	%(***PERFORM TYPE-CONVERSION ON A CONSTANT****)%
	IF .ARGNODE[OPR1] EQL CONSTFL
	THEN
	BEGIN
		C1H = .ARGNODE[CONST1];
		C1L = .ARGNODE[CONST2];
		IF .CNODE[A2NOTFLG]	!IF MUST TAKE THE "NOT" OF THE ARG
		THEN
		BEGIN
			C1H = NOT .C1H;
			C1L = NOT .C1L;
		END;
		IF .CNODE[A2NEGFLG]	!IF MUST TAKE THE NEG OF THE ARG
		THEN
		BEGIN
			IF .ARGNODE[VALTYPE] EQL DOUBLPREC OR .ARGNODE[VALTYPE] EQL REAL
			THEN
			%(***FOR DOUBLE PREC (AND REAL) MUST USE ASSEMBLY LANG ROUTINE
				TO TAKE NEG***)%
			BEGIN
%761%				COPRIX = KDNEGB;
				CNSTCM();
				C1H = .C2H;
				C1L = .C2L;
			END
			ELSE
			BEGIN
				C1H = -.C1H;
				C1L = -.C1L;
			END
		END;
		COPRIX = KTPCNVIX(CNODE);
		CNSTCM();

		%(***SET THE VALFLG IN THE PARENT OF CNODE***)%
		SETPVAL(.CNODE);

		RETURN MAKECNST(.CNODE[VALTYPE],.C2H,.C2L);

	END;

	CNODE[ARG2PTR] = .ARGNODE;

	RETURN .CNODE;

END;	! of P2SKLTP

GLOBAL ROUTINE P2SKLARR(CNODE)=
%(********
	INITIAL PASS OF PHASE 2 SKELETON FOR AN ARRAY REFERENCE.
	THE EXPRESSION NODE FOR THE ARRAYREF IS ASSUMED TO HAVE THE
	FOLLOWING 2 ARGS:
		ARG1PTR - PTR TO THE SYMBOL TABLE ENTRY FOR THE ARRAY NAME
		ARG2PTR - PTR TO AN EXPRESSION NODE FOR THE ADDRESS CALCULATION
********)%
BEGIN
	MAP PEXPRNODE CNODE;
	LOCAL PEXPRNODE SSNODE;
	LOCAL PRVNEGFLG,PRVNOTFLG;

	DEBGNODETST(CNODE);		!FOR DEBUGGING ONLY


	%(*****UNLESS THE ADDRESS-CALCULATION IS A LEAF, PERFORM THE
		PHASE 2 SKEL OPTIMIZATIONS ON IT****)%
	IF NOT .CNODE[A2VALFLG]
	THEN
	BEGIN
		SSNODE = .CNODE[ARG2PTR];
		PRVNEGFLG = .NEGFLG;
		PRVNOTFLG = .NOTFLG;
		NEGFLG = FALSE;
		NOTFLG = FALSE;
		CNODE[ARG2PTR] = (.P2SKL1DISP[.SSNODE[OPRCLS]])(.SSNODE);
		CNODE[A2NEGFLG] = .NEGFLG<0,1>;
		CNODE[A2NOTFLG] = .NOTFLG<0,1>;
		NEGFLG = .PRVNEGFLG;		!CANNOT PASS NEG/NOT DOWN OVER AN
						! ARRAYREF NODE; HENCE IF WERE TRYING TO DO SO,
						! PASS THEM BACK UP TO PARENT
		NOTFLG = .PRVNOTFLG;
	END;

	RETURN	.CNODE;

END;	! of P2SKLARR


GLOBAL ROUTINE P2SKNEGNOT(CNODE)=
%(***************************************************************************
	INITIAL PASS OF PHASE 2 SKEL FOR A NEG OR NOT NODE
	TRANSFORMS:
		-(-X)=X
		NOT(NOT X)=X
	PERFORMS NEG/NOT ON A CONSTANT
	PASSES NEG AND NOT ON DOWN TO BOTTOMMOST NODES
	IN MANY CASES
	WHEN A NEG/NOT CANNOT BE PASSED DOWN ANY FURTHER, THE PARENT
	NODE HAS A FLAG SET INDICATING "NEGATE(OR COMPLEMENT) THE
	FIRST (OR 2ND) ARG"; 
	THE NEGATE/NOT NODE IS REMOVED FROM THE TREE.
	A NEGATE CANNOT BE PASSED DOWN FROM ABOVE OVER A NOT. IF THIS
	SITUATION ARISES (EG -(NOT X)), THE NEG WILL BE PASSED BACK UP
	WHEN THE NOT IS ENCOUNTERED AND IF THE NOT CANNOT BE PROPAGATED DOWN
	THE NOT NODE MUST BE LEFT IN THE TREE.
	SIMILARLY, A  NOT CANNOT BE PROPAGATED OVER A NEGATE.

	WHEN A NEGATE OR NOT CANNOT BE PROPAGATED DOWNWARD, THEN
	DEPENDING ON WHAT THE PARENT NODE OVER THE NEG/NOT NODE IS, THE NEG OR
	NOT MAY IN SOME CASES BE PROPAGATED BACK UPWARD.
***************************************************************************)%
BEGIN
	MAP PEXPRNODE CNODE;
	LOCAL PEXPRNODE ARGNODE;
	OWN PEXPRNODE PARNODE;			!PTR TO PARENT NODE

	%(***DEFINE MACRO TO REMOVE THE NEG/NOT NODE FROM THE TREE***)%
	MACRO REMOVE=
	BEGIN
		%(***IF ARG IS A LEAF, SET VALFLG IN PARENT OF CNODE***)%
		IF .ARGNODE[OPRCLS] EQL DATAOPR
			OR .ARGNODE[OPRCLS] EQL REGCONTENTS
		THEN
		BEGIN
			 SETPVAL(.CNODE);

			%(***IF THE IMMEDIATE-FLAG WAS SET IN THE NEG/NOT NODE, SET IT
				IN THE PARENT OF THE NEG/NOT NODE***)%
			IF .CNODE[A2IMMEDFLG]
			THEN SETPIMMED(.CNODE);

		END

		%(***OTHERWISE SET PARENT PTR OF THE ELEMENT BELOW CNODE
			AND IF HAVE A PARENFLG ON CNODE, PUT IT ON THE ELEMENT BELOW**)%
		ELSE
		BEGIN
			 ARGNODE[PARENT] = .CNODE[PARENT];
			IF .CNODE[PARENFLG] THEN ARGNODE[PARENFLG] = 1;
		END;
		RETURN .ARGNODE;
	END$;

	%(***DEFINE A MACRO TO LEAVE A NEG NODE IN THE TREE, AND RETURN WITH NEGFLG=FALSE***)%
	MACRO LEAVENEG=
	BEGIN
		NEGFLG = FALSE;
		CNODE[OPERSP] = NEGOP;	!THIS NODE MAY HAVE ORIGINALLY BEEN A NOT.
					! EG .NOT.(.NOT.(-X))
		RETURN .CNODE;
	END$;

	%(***DEFINE A MACRO TO LEAVE A NOT NODE IN THE TREE, AND RETURN WITH NOTFLG=FALSE***)%
	MACRO LEAVENOT=
	BEGIN
		NOTFLG = FALSE;
		CNODE[OPERSP] = NOTOP;	!THIS NODE MAY HAVE ORIGINALLY BEE A NEG.
					! EG -(-(.NOT.X))
		RETURN .CNODE;
	END$;



	DEBGNODETST(CNODE);		!FOR DEBUGGING ONLY

	ARGNODE = .CNODE[ARG2PTR];

	IF .CNODE[OPERSP] EQL NEGOP
	THEN
	%(***IF CNODE IS A 'NEG' NODE (UNARY MINUS)***)%
	BEGIN
		%(***IF WERE TRYING TO PROPAGATE A 'NOT' FROM ABOVE
			CANNOT PROPAGATE IT ACROSS A NEG NODE***)%
		IF .NOTFLG
		THEN
		RETURN NOTOFNEG(.CNODE);

		NEGFLG = NOT .NEGFLG;
	END

	ELSE
	IF .CNODE[OPERSP] EQL NOTOP
	THEN
	%(***IF CNODE IS A 'NOT' NODE***)%
	BEGIN
		IF  .NEGFLG
		THEN
		%(***IF WERE TRYING TO PROPAGATE A 'NEG' FROM ABOVE,
			CANNOT PROPAGATE IT ACROSS A 'NOT' NODE***)%
		RETURN NEGOFNOT(.CNODE);
		NOTFLG = NOT .NOTFLG;
	END;


	IF .CNODE[A2VALFLG]
	THEN

	%(***IF THE ARGUMENT UNDER CNODE IS A LEAF***)%

	BEGIN

		%(****IF THE ARG IS A CONSTANT, CREATE A NEW CONSTANT TABLE ENTRY***)%
		IF .ARGNODE[OPR1] EQL CONSTFL
		THEN
		BEGIN
			IF .NEGFLG
			THEN
			%(****FOR NEG***)%
			BEGIN
				NEGFLG = FALSE;
				%(***SET THE VALFLG IN THE PARENT OF THE NEG***)%
				SETPVAL(.CNODE);
				RETURN NEGCNST(ARGNODE);
			END;

			IF .NOTFLG
			THEN
			%(****FOR NOT***)%
			BEGIN
				NOTFLG = FALSE;
				%(***SET THE VALFLG IN THE PARENT OF THE NOT***)%
				SETPVAL(.CNODE);
				RETURN NOTCNST(ARGNODE);
			END;
		END;
	END


	ELSE
	%(***IF ARG IS NOT A LEAF, TRY TO PROPAGATE NEG AND NOT OVER IT***********)%
	BEGIN
		ARGNODE =   (.P2SKL1DISP[.ARGNODE[OPRCLS]])(.ARGNODE);
		CNODE[ARG2PTR] = .ARGNODE;
	END;


	%(****IF ARE LEFT WITH A NEG OR NOT THAT COULD NOT BE PROPAGATED DOWN, DECIDE
		WHETHER OR NOT TO COLLAPSE IT UP INTO THE PARENT ON THE BASIS
		OF THE OPERATOR CLASS OF THE PARENT
	*******)%

	IF .NEGFLG
	THEN
	BEGIN
		IF TAKNEGARG(.CNODE[PARENT])
		THEN
		REMOVE
		ELSE
		LEAVENEG;

	END

	ELSE

	%(***IF HAVE A NOT THAT WERE UNABLE TO PROPAGATE DOWN***)%
	IF .NOTFLG
	THEN
	BEGIN
		IF TAKNOTARG(.CNODE[PARENT])	!IF THE NOT CAN BE ABSORBED BY THE PARENT
		THEN REMOVE			! REMOVE THE NOT NODE AND PROPAGATE
						! THE NOT UP TO THE PARENT
		ELSE LEAVENOT;			!OTHERWISE LEAVE THE NOT NODE
	END


	%(***IF THE NEG OR NOT WAS ABSORBED BELOW THIS NODE, CAN REMOVE THE NEG/NOT NODE
		FROM THE TREE****)%
	ELSE
	REMOVE;

END;	! of P2SKNEGNOT



ROUTINE P2SKCONCAT(CNODE) =
BEGIN

%1474%	! Written by TFV on 8-Feb-82

	! Perform skeleton optimizations  on CONCATENATION nodes.   Walk
	! down  the  argument  list  performing  optimizations  on   the
	! arguments (except for  the first which  is the descriptor  for
	! the result).  If the lengths  are fixed, change the OPERSP  to
	! CONCTF.  If the maximum length of the result is known,  change
	! the OPERSP to CONCTM.


	MAP BASE CNODE;

	REGISTER
		ARGUMENTLIST ARGLIST,	! Pointer to the argument list
		PEXPRNODE ARGNODE;	! Pointer to an argument

	LOCAL
		PEXPRNODE ANODE,	! Pointer to  an  arrayref  node
					! under a substring node
		PEXPRNODE LNODE,	! Lower bound of a substring node
		PEXPRNODE UNODE,	! Upper bound of a substring node
		ISFIXEDLEN,		! Flag  for  this  concatenation
					! has a fixed length
		ISMAXLEN,		! Flag  for  this  concatenation
					! has a known maximum length
		HASSUBCONC,		! Flag   for   this   node   has
					! concatenations as subnodes
		LEN,			! Size of the fixed length result
		NUMARGS,		! Number  of  arguments  for  the
					! folded concatenation.
		ARGUMENTLIST DOWNARGL,	! Pointer to the argument list of
					! a subnode
		PEXPRNODE SUBARG,	! Pointer to the argument in  the
					! argument list of a subnode
		ARGUMENTLIST NEWARGL,	! Pointer to  the  new  argument
					! list    used    when    moving
					! concatenation   subexpressions
					! to top level
		NEWARGPOS;		! Pointer to  the next  position
					! to fill  in the  new  argument
					! list

	NUMARGS = 1;			! One argument is  needed for the
					! result of the concatenation
	LEN = 0;			! Initialize length
	HASSUBCONC = FALSE;		! Assume no  concatenations below
					! this node
	ISFIXEDLEN = TRUE;		! Assume this  is a fixed  length
					! concatenation
	ISMAXLEN = TRUE;		! Assume this  is a concatenation
					! with a known maximum length

	ARGLIST = .CNODE[ARG2PTR];	! Get a pointer to the argument list

	INCR I FROM 2 TO .ARGLIST[ARGCOUNT]
	DO
	BEGIN	! Walk down the argument list

		! Walk down the  arguments from the  second onward.   Do
		! the skeleton optimization for each sub-expression.

		! The length of each legal  argument MUST be added  into
		! LEN so that  we can  assign the length  of the  concat
		! needed.

		ARGNODE = .ARGLIST[.I, ARGNPTR];	! Pointer to the
							! argument

		! If this  argument  is  not a  DATAOPR,  walk  down  it
		! performing further skeleton optimizations.

		IF NOT .ARGLIST[.I, AVALFLG]
		THEN ARGLIST[.I, ARGNPTR] = ARGNODE =
			 (.P2SKL1DISP[.ARGNODE[OPRCLS]])(.ARGNODE);

		! Now process this argument based on OPRCLS.

		CASE .ARGNODE[OPRCLS] OF SET

		CGERR();	! BOOLEAN - error

		BEGIN		! DATAOPR

			! The argument either was a DATAOPR or it became
			! one through folding.

			ARGLIST[.I, AVALFLG] = 1;	! Set flag bit

			! If this is a  fixed length result, update  the
			! length.  Otherwise reset the fixed length  and
			! maximum length flags.

			IF .ARGNODE[OPERATOR] NEQ CHARCONST
			THEN
			BEGIN	! Variable

				IF .ARGNODE[IDCHLEN] EQL LENSTAR
				THEN
				BEGIN
					ISFIXEDLEN = FALSE;
					ISMAXLEN = FALSE;
				END
				ELSE	LEN = .LEN + .ARGNODE[IDCHLEN];

			END	! Variable
			ELSE	! Constant
				LEN = .LEN + .ARGNODE[LITLEN];

			! Update the argument count

			NUMARGS = .NUMARGS + 1;

		END;		! DATOPR 

		CGERR();	! RELATIONAL - error

		BEGIN		! FNCALL

			! Look  at  the  symbol  table  entry  for   the
			! function name to get the length of the result.
			! It can not have length *.

			SUBARG = .ARGNODE[ARG1PTR];
			LEN = .LEN + .SUBARG[IDCHLEN];

			! Update the argument count

			NUMARGS = .NUMARGS + 1;

		END;		! FNCALL

		CGERR();	! ARITHMETIC - error
		CGERR();	! TYPCNV - error

		BEGIN		! ARRAYREF

			! Get the pointer to the array name

			SUBARG = .ARGNODE[ARG1PTR];

			! If this  is  a  fixed length  array,  get  the
			! length of  an  element  from  the  array  name
			! symbol table entry.  Otherwise reset the fixed
			! length and maximum length flags.

			IF .SUBARG[IDCHLEN] EQL LENSTAR
			THEN
			BEGIN
				ISFIXEDLEN = FALSE;
				ISMAXLEN = FALSE;
			END
			ELSE	LEN = .LEN + .SUBARG[IDCHLEN];

			! Update the argument count

			NUMARGS = .NUMARGS + 1;

		END;		! ARRAYREF

		CGERR();	! CMNSUB - character common subs are not
				! supported in this release.

		CGERR();	! NEGNOT - error
		CGERR();	! SPECOP - error
		CGERR();	! FIELDREF - error
		CGERR();	! STORECLS - error
		CGERR();	! REGCONTENTS - error
		CGERR();	! LABOP - error
		CGERR();	! STATEMENT - error
		CGERR();	! IOLSCLS - error

%1655%		BEGIN		! INLINFN
%1655%
%1655%			! Add in the  length of the  inline function  to
%1655%			! the  concat  node.   Do  this  before  calling
%1655%			! P2SILF,  since  it  could   come  back  as   a
%1655%			! constant, and we don't want to bother what  it
%1655%			! gets optimized into to get the length.
%1655%
%1655%			SUBARG = .ARGNODE[ARG2PTR];	! .Dnnn return value
%1655%			LEN = .LEN + .SUBARG[IDCHLEN];	! Add in length
%1655%
%1655%			ARGLIST[.I,ARGNPTR] = P2SILF(.ARGNODE);	! Optimize
%1655%
%1655%		END;		! INLINFN

		BEGIN		! SUBSTRING

			!!! Will need more code to support the  A(I:I+3)
			!!! case.   This   is   also  a   fixed   length
			!!! concatenation.

			! Get pointer to upper bound expression

			UNODE = .ARGNODE[ARG1PTR];

			! Get pointer to lower bound expression

			LNODE = .ARGNODE[ARG2PTR];

			! Get pointer to ARRAYREF or DATAOPR node

			ANODE = .ARGNODE[ARG4PTR];

			! If both substring  bounds are constants,  this
			! is a fixed length concatenation.  Otherwise if
			! the DATAOPR or ARRAYREF subnode is not  length
			! * it is a known maximum length.

			IF .LNODE[OPR1] EQL CONSTFL AND
				.UNODE[OPR1] EQL CONSTFL
			THEN
			BEGIN	! Fixed length result

				LEN = .LEN + .UNODE[CONST2] - .LNODE[CONST2];

			END	! Fixed length result
			ELSE
			BEGIN	! Maximum or dynamic length result

				! Reset the fixed length flag

				ISFIXEDLEN = FALSE;

				! If this is an ARRAYREF, get the symbol
				! table entry for  the identifier  under
				! it.

				IF .ANODE[OPRCLS] EQL ARRAYREF
				THEN ANODE = .ANODE[ARG1PTR];

				IF .ANODE[IDCHLEN] EQL LENSTAR
				THEN
				BEGIN	! Dynamic length

					ISMAXLEN = FALSE;

				END	! Dynamic length
				ELSE	LEN = .LEN + .ANODE[IDCHLEN];

			END;	! Maximum or dynamic length result

			! Update the argument count

			NUMARGS = .NUMARGS + 1;

		END;		! SUBSTRING

		BEGIN		! CONCATENATION

			! Set the  flag  for  there  are  concatenations
			! under this node.

			HASSUBCONC = TRUE;

			IF .ARGNODE[OPERSP] EQL CONCTF
			THEN
			BEGIN	! Fixed length concatenation

				! This is a  fixed length  concatenation
				! as a sub-expression.   Get the  length
				! of   the   result   of   the   subnode
				! concatenation.  It is a constant table
				! entry pointed to by ARG1PTR.

				SUBARG = .ARGNODE[ARG1PTR];
				LEN = .LEN + .SUBARG[CONST2];

			END	! Fixed length concatenation
			ELSE	IF .ARGNODE[OPERSP] EQL CONCTM
			THEN
			BEGIN	! Known maximum length

				! Reset the fixed length flag

				ISFIXEDLEN = FALSE;

				! This is a maximum length concatenation
				! as a sub-expression.   Get the  length
				! of   the   result   of   the   subnode
				! concatenation.  It is a constant table
				! entry pointed to by ARG1PTR.

				SUBARG = .ARGNODE[ARG1PTR];
				LEN = .LEN + .SUBARG[CONST2];

			END	! Known maximum length
			ELSE
			BEGIN	! Dynamic length

				! Reset the  fixed  length  and  maximum
				! length flags

				ISFIXEDLEN = FALSE;
				ISMAXLEN = FALSE;

			END;	! Dynamic length

			! Get a  pointer to  the  argument list  of  the
			! subnode.

			DOWNARGL = .ARGNODE[ARG2PTR];

			! Update the  count  of  the  actual  number  of
			! concatenation arguments.   The first  argument
			! is ignored  since it  is  the result  for  the
			! concatenation subnode.

			NUMARGS = .NUMARGS + .DOWNARGL[ARGCOUNT] - 1;

		END;		! CONCATENATION

		TES;

	END;	! Walk down the argument list

	IF .ISFIXEDLEN
	THEN
	BEGIN	! Fixed length result

		CNODE[OPERSP] = CONCTF;

		! Fill in ARG1PTR with a  pointer to the constant  table
		! entry for the length.

		CNODE[ARG1PTR] = MAKECNST(INTEGER, 0, .LEN);

	END	! Fixed length result
	ELSE	IF .ISMAXLEN
	THEN
	BEGIN	! Known maximum length

		CNODE[OPERSP] = CONCTM;

		! Fill in ARG1PTR with a  pointer to the constant  table
		! entry for the maximum length of the result.

		CNODE[ARG1PTR] = MAKECNST(INTEGER, 0, .LEN);

	END;	! Known maximum length

	IF .HASSUBCONC
	THEN
	BEGIN	! Concatenations under this node

		! There are concatenations under this node, build a  new
		! node  with  all  the  concatenations  at  top   level.
		! Compute the size needed for the new argument block

		NAME<LEFT> = ARGLSTSIZE(.NUMARGS);

		NEWARGL = CORMAN();	! Get space for the new argument
					! block.

		! Copy the header words to the new argument list

		DECR I FROM ARGHDRSIZ - 1 TO 0
		DO (.NEWARGL)[.I] = .(.ARGLIST)[.I];

		! Fill in the argument count

		NEWARGL[ARGCOUNT] = .NUMARGS;

		! Walk down the old argument list copying the  arguments
		! into the  new argument  list.  Move  the arguments  of
		! concatenation subnodes to the top level.  Do not  copy
		! the first arguments of concatenations since these  are
		! the decriptors for the result.

		NEWARGPOS = 2;		! Start filling the new argument
					! block at  the second  argument
					! position

		INCR I FROM 2 TO .ARGLIST[ARGCOUNT]
		DO
		BEGIN	! Walk the old argument list			

			! Get the pointer to the next argument

			ARGNODE = .ARGLIST[.I, ARGNPTR];

			IF .ARGNODE[OPRCLS] EQL CONCATENATION
			THEN
			BEGIN	! Concatenation subexpression

				! Get the pointer  to the argument  list
				! of the concatenation subnode

				DOWNARGL = .ARGNODE[ARG2PTR];

				INCR J FROM 2 TO .DOWNARGL[ARGCOUNT]
				DO
				BEGIN	! Copy arguments to top level

					NEWARGL[.NEWARGPOS, ARGFULL] = .DOWNARGL[.J, ARGFULL];

%1641%					! If this arg has a parent pointer, (is
%1641%					! not an STE), then change it to  point
%1641%					! to the upper concatenation node.

%1641%					IF NOT .NEWARGL[.NEWARGPOS,AVALFLG]
%1641%					THEN
%1641%					BEGIN
%1641%						SUBARG = .NEWARGL[.NEWARGPOS,ARGNPTR];
%1641%						SUBARG[PARENT] = .CNODE
%1641%					END;

					! Update   position    in    new
					! argument list

					NEWARGPOS = .NEWARGPOS + 1;

				END;	! Copy arguments to top level

				! Free the space  for the argument  list
				! of the concatenation subnode

				SAVSPACE(ARGLSTSIZE(.DOWNARGL[ARGCOUNT]) - 1, .DOWNARGL);

				! Free the space  for the  concatenation
				! subnode

				SAVSPACE(EXSIZ - 1, .ARGNODE)

			END	! Concatenation subexpression
			ELSE
			BEGIN	! Not Concatenation

				! Just copy  this  argument to  the  new
				! argument list

				NEWARGL[.NEWARGPOS, ARGFULL] = .ARGLIST[.I, ARGFULL];

				! Update position in new argument list

				NEWARGPOS = .NEWARGPOS + 1;

			END;	! Not Concatenation

		END;	! Walk the old argument list			

		! Free the space for the old argument list

		SAVSPACE(ARGLSTSIZE(.ARGLIST[ARGCOUNT]) - 1, .ARGLIST);

		! Link in the new argument list

		CNODE[ARG2PTR] = .NEWARGL;

	END;	! Concatenations under this node

	RETURN .CNODE		! Return the new node

END;	! of P2SKCONCAT



ROUTINE P2SKSUBSTR(CNODE)=		![1431] New

%(**********************************************************************
	PHASE 2 SKELETON FOR A SUBSTRING NODE
	TRANSFORMS SUBSTRINGS WITH CONSTANT BOUNDS INTO .D VARIABLES
NYI	TRANSFORMS NODES TO LOWER/LENGTH FORM INSTEAD OF LOWER/UPPER FORM
NYI	IMPROVES NODES WITH CONSTANT LOWER BOUNDS THAT .NE. 1 TO ONES
		(LENGTH FORM?) WITH LOWER BOUND .EQ. 1.
**********************************************************************)%

BEGIN
	MAP PEXPRNODE CNODE;
	REGISTER PEXPRNODE LNODE:UNODE:ANODE;
	LOCAL PEXPRNODE DVAR;
	LOCAL PRVNEGFLG,PRVNOTFLG;
%1557%	LOCAL PEXPRNODE CHLEN;

	DEBGNODETST(CNODE);		! For debugging only
	
	! Perform skel optimizations on offspring nodes

	UNODE = .CNODE[ARG1PTR];	! UNODE points to upper bound expr
	LNODE = .CNODE[ARG2PTR];	! LNODE points to lower bound-1 expr
	ANODE = .CNODE[ARG4PTR];	! ANODE points to ARRAYREF or DATAOPR

	IF NOT .CNODE[A1VALFLG]
	THEN
	BEGIN	! do U node
		PRVNEGFLG = .NEGFLG;	! Cannot pass neg/not down over
		PRVNOTFLG = .NOTFLG;	!   substring, so stop them here
		NEGFLG = NOTFLG = FALSE; !  and pass them back up to parent.
		CNODE[ARG1PTR] = UNODE = (.P2SKL1DISP[.UNODE[OPRCLS]])(.UNODE);
		CNODE[A1NEGFLG] = .NEGFLG;
		CNODE[A1NOTFLG] = .NOTFLG;
		NEGFLG = .PRVNEGFLG;
		NOTFLG = .PRVNOTFLG;
	END;	! do U node

	IF NOT .CNODE[A2VALFLG]
	THEN
	BEGIN	! do L node
		PRVNEGFLG = .NEGFLG;
		PRVNOTFLG = .NOTFLG;
		NEGFLG = NOTFLG = FALSE;
		CNODE[ARG2PTR] = LNODE = (.P2SKL1DISP[.LNODE[OPRCLS]])(.LNODE);
		CNODE[A2NEGFLG] = .NEGFLG;
		CNODE[A2NOTFLG] = .NOTFLG;
		NEGFLG = .PRVNEGFLG;
		NOTFLG = .PRVNOTFLG;
	END;	! do L node

	IF .ANODE[OPRCLS] EQL ARRAYREF
	THEN
	CNODE[ARG4PTR] = ANODE = (.P2SKL1DISP[.ANODE[OPRCLS]])(.ANODE);

%1557%	! Get length of variable we're taking substring of

%1557%	IF .ANODE[OPRCLS] NEQ ARRAYREF
%1557%	THEN CHLEN = .ANODE[IDCHLEN]
%1557%	ELSE (CHLEN = .ANODE[ARG1PTR]; CHLEN = .CHLEN[IDCHLEN]);

%1557%	! If bounds are constant, check that they're in range

%1522%	IF .LNODE[OPR1] EQL CONSTFL	! If lower bound is constant
%1522%	THEN IF .LNODE[CONST2] LSS 0	! it must be at least 1
%1522%	THEN FATLERR(.ISN,E165<0,0>);	! "Substring bound out of range"

%1557%	IF .UNODE[OPR1] EQL CONSTFL	! If upper bound is constant
%1557%	THEN IF .UNODE[CONST2] GTR .CHLEN ! it must be less than string length
%1557%	THEN IF .CHLEN NEQ LENSTAR	! (if length is known)
%1557%	THEN FATLERR(.ISN,E165<0,0>);	! "Substring bound out of range"

	! Turn reference into a .D variable if both bounds are constant and
	! if the base variable is a simple (non-formal) scalar.

%1452%	IF .ANODE[OPR1] EQL VARFL	
%1706%	THEN 	IF NOT .ANODE[IDATTRIBUT(DUMMY)]
%1706%	THEN
	BEGIN	! Scalar

		! If the substring has constants for both bounds, we can
		! replace the substring with a .D variable (whose descriptor
		! is calculated at compile time).

		IF .LNODE[OPR1] EQL CONSTFL
		THEN
		BEGIN	! Lower bound constant
			IF .UNODE[OPR1] EQL CONSTFL
			THEN
			BEGIN	! Lower and upper bound both constant

				! Substitute a 2-word .D variable for the
				! substring reference.  Get the .D variable's
				! address from the base variable's address;
				! calculate its length and byte offset now.

				DVAR = NEWDVAR(1); 
				DVAR[IDADDR] = .CNODE[ARG4PTR];
				DVAR[IDCHLEN] = .UNODE[CONST2] - .LNODE[CONST2];

%1522%				! Give  substring  bound  out  of  range
%1522%				! error if  upper  bound  is  less  than
%1522%				! lower bound.

%1522%				IF .DVAR[IDCHLEN] LEQ 0
%1522%				THEN FATLERR(.ISN,E165<0,0>);

				DVAR[IDBPOFFSET] = .LNODE[CONST2];
				RETURN .DVAR;
			END;	! Lower and upper bound both constant
		END;	! Lower bound constant

	END;	! Scalar

	RETURN .CNODE;

END;	! of P2SKSUBSTR

GLOBAL ROUTINE P2SILF(CNODE)=
BEGIN

! Try to  change  a  function  call into  an  inline  function  or  type
! conversion node.  It may be instead  optimized into a constant, or  it
! may be decided to keep the original function call.

! Returns:	CNODE

! [1567] New with code moved from P2SKFN


	MAP	BASE CNODE;		! Function call node to look at

	REGISTER
		ARGUMENTLIST ARGLST,	! Argument list
		BASE ARGNODE;		! Argument node

	LOCAL	BASE FNNAMENTRY,	! Function symbol table entry
		ARGLEN,			! Length of a char arg
		ARGPOS,			! Position of arg in arg list
		BASE OLDCNODE;		! Node to be removed


	ARGLST = .CNODE[ARG2PTR];
	FNNAMENTRY = .CNODE[ARG1PTR];


	! "In release 1, we don't expand anything with more than 2  args
	! inline"  Don't make this inline.
	IF .ARGLST[ARGCOUNT] GTR 2 THEN RETURN .CNODE;


	! Character fn's arg is the 2nd in the arg list.
	IF .CNODE[VALTYPE] EQL CHARACTER
	THEN	ARGNODE = .ARGLST[2,ARGNPTR]
	ELSE	ARGNODE = .ARGLST[1,ARGNPTR];


	! If possible, fold these calls into constants now.

	IF .FNNAMENTRY[IDFNFOLD]
	THEN
	BEGIN
%1535%		! Try to optimize library functions
%1535%
%1535%		! If CHAR or ICHAR with a constant argument, we want  to
%1535%		! optimize this, returning the  value we would have  had
%1535%		! to calculate at run-time.
%1535%
%1535%		IF .FNNAMENTRY[IDINLINOPR] EQL ICHARFNOP
%1535%		THEN
%1535%		BEGIN	! ICHAR
%1535%
%1535%			IF .ARGNODE[OPR1] EQL CONSTFL
%1535%			THEN
			BEGIN
				SETPVAL(.CNODE);	! Set parent
				SAVSPACE(EXSIZ-1,.CNODE);
%1535%				RETURN MAKECNST(INTEGER, 0, .ARGNODE[LITC2]);
%1535%			END;
%1535%
%1535%		END	! ICHAR
%1535%
%1535%		ELSE
%1535%		IF .FNNAMENTRY[IDINLINOPR] EQL CHARFNOP
%1535%		THEN
%1535%		BEGIN	! CHAR
%1535%
%1535%			IF .ARGNODE[OPR1] EQL CONSTFL
%1535%			THEN
%1535%			BEGIN	! Make a character constant
%1535%
				SAVSPACE(EXSIZ-1,.CNODE);	! Freespace

				! Make a literal constant
%1535%				CNODE = MAKLIT(1);
%1535%				CNODE[LIT1] = ASCII'     ';
%1535%				CNODE[LITC2] = .ARGNODE[CONST2]; ! Const value
%1535%
%1535%				! If out of bounds, give a warning.
%1535%				IF .ARGNODE[CONST2] LSS 0 OR
%1535%					.ARGNODE[CONST2] GTR #177
%1535%				THEN 	FATLERR(.ISN,E202<0,0>);
%1535%
				! .Dnnn is not used, don't allocate it.
				ARGNODE = .ARGLST[1,ARGNPTR];
				ARGNODE[IDATTRIBUT(NOALLOC)] = 1;

				SETPVAL(.CNODE);		! Set parent
%1535%				RETURN .CNODE;
%1535%
%1535%			END;	! Make a character constant

%1535%		END	! CHAR

		ELSE
		IF .FNNAMENTRY[IDINLINOPR] EQL LENFNOP
		THEN
		BEGIN	! LEN
		
			! If we can find out the length of the character
			! argument  at  compile-time,  then  remove  the
			! function call and make this a constant node.

			! Make sure we don't have an array ref since  we
			! could have a function call under this node and
			! not know it yet.  (If  there is a fn call,  it
			! must be done)

			IF .ARGNODE[OPRCLS] NEQ ARRAYREF
			THEN
			BEGIN
				ARGLEN = CHEXLEN(.ARGNODE);	! Len of arg
				IF .ARGLEN NEQ LENSTAR		! Len known?
				THEN
				BEGIN
					SETPVAL(.CNODE);	! Set parent
					SAVSPACE(EXSIZ-1,.CNODE); ! Freespace
					RETURN MAKECNST(INTEGER, 0, .ARGLEN);
				END;
			END;

		END;	! LEN

	END;	! Try to fold into a constant


	! Make into either a Type convert or an In line function node

	IF .FNNAMENTRY[IDILFOPRCLS] EQL TYPECNV
	THEN
	BEGIN	! Type conversion

		CNODE[OPERATOR] = .FNNAMENTRY[IDINLINOPR];

		%(***For a type-conversion node, the single arg is arg2***)%
		CNODE[ARG2PTR] = .ARGNODE;
		IF .ARGLST[1,AVALFLG] THEN CNODE[A2VALFLG] = 1;

%1264%		! If a type conversion NOP  (the from and to values  are
%1264%		! the same), then remove the node, and replace the  type
%1264%		! conversion node with the argument.
%1264%
%1264%		IF .CNODE[VALTP2] EQL .CNODE[OPERSP]
%1264%		THEN
%1264%		BEGIN	! Remove type-convert
%1264%
%1264%			OLDCNODE  =  .CNODE;	! Node to remove
%1264%
%1264%			! Make new node from argument for function
%1264%			CNODE  =  .CNODE[ARG2PTR];
%1264%
%1264%			! Set up parent depending on whether new node is
%1264%			! leaf
%1264%
%1264%			IF .CNODE[OPRCLS] EQL DATAOPR
%1264%				OR .CNODE[OPRCLS] EQL REGCONTENTS
%1264%				OR .CNODE[OPRCLS] EQL CMNSUB
%1264%			THEN	SETPVAL(.OLDCNODE)	% Leaf %
%1264%			ELSE	CNODE[PARENT]  =  .OLDCNODE[PARENT];
%1264%
			! Free up the space
			SAVSPACE(EXSIZ-1,.OLDCNODE);

			RETURN .CNODE;

%1264%		END;	! Remove type-convert

	END	! Type convert
	ELSE
	BEGIN	! In-line

		! If either argument  is octal, then  we shouldn't  make
		! this into an inline function.  Return the node passed.

		INCR CNT FROM 1 TO .ARGLST[ARGCOUNT]
		DO
		BEGIN
			ARGNODE = .ARGLST[.CNT,ARGNPTR];
%1273%			IF (.ARGNODE[VALTYPE] EQL OCTAL)
%1273%				OR (.ARGNODE[VALTYPE] EQL DOUBLOCT)
			THEN	RETURN .CNODE;	! Don't make inline
		END;

		! Change opr fields to be inline
		CNODE[OPERATOR] = .FNNAMENTRY[IDINLINOPR];

		! Inline's argument position  in the arglist depends  on
		! whether the function is character.
		IF .CNODE[VALTYPE] EQL CHARACTER
		THEN	ARGPOS = 2
		ELSE	ARGPOS = 1;

		! Set up arg1 and whether it is a leaf
		CNODE[ARG1PTR] = .ARGLST[.ARGPOS,ARGNPTR];
		CNODE[A1VALFLG] = .ARGLST[.ARGPOS,AVALFLG];

		IF .ARGLST[ARGCOUNT] EQL 2
		THEN
		BEGIN	! 2 arguments

			! If a character function,  a .Dnnn variable  is
			! needed for  the return  value.  Save  the  one
			! originally generated for the function's return
			! value, before the arglist is returned to  free
			! space.

			IF .CNODE[VALTYPE] EQL CHARACTER
			THEN	ARGPOS = 1
			ELSE	ARGPOS = 2;

			CNODE[ARG2PTR] = .ARGLST[.ARGPOS,ARGNPTR];
			CNODE[A2VALFLG] = .ARGLST[.ARGPOS,AVALFLG];
		END
		ELSE
		BEGIN	! One argument

			CNODE[ARG2PTR] = 0;
		END;

	END;	! In-line


	%(***If arg1 under this node has a neg node as its top node,
		fold it out***)%
	ARGNODE = .CNODE[ARG1PTR];
	IF .ARGNODE[OPR1] EQL NEGFL
	THEN
	BEGIN
		CNODE[A1NEGFLG] = 1;
		CNODE[ARG1PTR] = .ARGNODE[ARG2PTR];
		IF .ARGNODE[A2VALFLG]
		THEN CNODE[A1VALFLG] = 1
		ELSE
		BEGIN
			OWN PEXPRNODE ARG1NODE;
			ARG1NODE = .CNODE[ARG1PTR];
			ARG1NODE[PARENT] = .CNODE;
		END;
		%(***Return the space for the neg to free storage***)%
		SAVSPACE(EXSIZ-1,.ARGNODE);
	END;


	! Return the core that was used for the arg list to free
	! storage.  Return # of wds-1.
	SAVSPACE(.ARGLST[ARGCOUNT]+ARGHDRSIZ-1,.ARGLST);

	RETURN .CNODE;

END;	! of P2SILF

END
ELUDOM