{$optimize 7}
{---------------------------------------------------------------}
{                                                               }
{  DAG Creation							}
{                                                               }
{  Places intermediate codes into DAGs and trees.		}
{                                                               }
{---------------------------------------------------------------}

unit DAG;

interface

{$segment 'DAG'}

{$LibPrefix '0/obj/'}

uses CCommon, CGI, CGC, Gen;

{---------------------------------------------------------------}

procedure DAG (code: icptr);

{ place an op code in a DAG or tree				}
{                                                               }
{ parameters:                                                   }
{       code - opcode						}


function TypeOf (op: icptr): baseTypeEnum;

{---------------------------------------------------------------}

implementation

var
   c_ind: iclist;			{vars that can be changed by indirect stores}
   maxLoc: integer;			{max local label number used by compiler}
   memberOp: icptr;			{operation found by Member}
   optimizations: array[pcodes] of integer; {starting indexes into peeptable}
   peepTablesInitialized: boolean;	{have the peephole tables been initialized?}
   rescan: boolean;			{redo the optimization pass?}

{-- External unsigned math routines; imported from Expression.pas --}

function udiv (x,y: longint): longint; extern;

function umod (x,y: longint): longint; extern;

function umul (x,y: longint): longint; extern;

function lshr (x,y: longint): longint; extern;

{-- External 64-bit math routines; imported from Expression.pas --}
{ Procedures for arithmetic and shifts compute "x := x OP y".   }

procedure umul64 (var x: longlong; y: longlong); extern;

procedure udiv64 (var x: longlong; y: longlong); extern;

procedure div64 (var x: longlong; y: longlong); extern;

procedure umod64 (var x: longlong; y: longlong); extern;

procedure rem64 (var x: longlong; y: longlong); extern;

procedure add64 (var x: longlong; y: longlong); extern;

procedure sub64 (var x: longlong; y: longlong); extern;

procedure shl64 (var x: longlong; y: integer); extern;

procedure ashr64 (var x: longlong; y: integer); extern;

procedure lshr64 (var x: longlong; y: integer); extern;

{---------------------------------------------------------------}

function CodesMatch (op1, op2: icptr; exact: boolean): boolean;

{ Check to see if the trees op1 and op2 are equivalent		}
{								}
{ parameters:							}
{    op1, op2 - trees to check					}
{    exact - is an exact match of operands required?		}
{								}
{ Returns: True if trees are equivalent, else false.		}


   function LongStrCmp (s1, s2: longStringPtr): boolean;

   { Are the strings s1 and s2 equal?				}
   {								}
   { parameters:						}
   {    s1, s2 - strings to compare				}
   {								}
   { Returns: True if the strings are equal, else false		}

   label 1;

   var
      i: integer;			{loop/index variable}

   begin {LongStrCmp}
   LongStrCmp := false;
   if s1^.length = s2^.length then begin
      for i := 1 to s1^.length do
         if s1^.str[i] <> s2^.str[i] then
            goto 1;
      LongStrCmp := true;
      end; {if}
1:
   end; {LongStrCmp}


   function OpsEqual (op1, op2: icptr): boolean;

   { See if the operands are equal				}
   {								}
   { parameters:						}
   {    op1, op2 - operations to check				}
   {								}
   { Returns: True if the operands are equivalent, else		}
   {    false.							}

   var
      result: boolean;			{temp result}

   begin {OpsEqual}
   result := false;
   case op1^.opcode of
      pc_cup, pc_cui, pc_tl1, pc_bno:
         {this rule prevents optimizations from removing sensitive operations}
         ;

      pc_adi, pc_adl, pc_adr, pc_and, pc_lnd, pc_bnd, pc_bal, pc_bor,
      pc_blr, pc_bxr, pc_blx, pc_equ, pc_neq, pc_ior, pc_lor, pc_mpi,
      pc_umi, pc_mpl, pc_uml, pc_mpr, pc_bqr, pc_bqx, pc_baq, pc_adq,
      pc_mpq, pc_umq: begin
         if op1^.left = op2^.left then
            if op1^.right = op2^.right then
               result := true;
         if not result then
            if op1^.left = op2^.right then
               if op1^.right = op2^.left then
                  result := true;
         if not result then
            if not exact then
               if CodesMatch(op1^.left, op2^.left, false) then
                  if CodesMatch(op1^.right, op2^.right, false) then
                     result := true;
         if not result then
            if not exact then
               if CodesMatch(op1^.left, op2^.right, false) then
                  if CodesMatch(op1^.right, op2^.left, false) then
                     result := true;
         end;

      otherwise: begin
         if op1^.left = op2^.left then
            if op1^.right = op2^.right then
               result := true;
         if not result then
            if not exact then
               if CodesMatch(op1^.left, op2^.left, false) then
                  if CodesMatch(op1^.right, op2^.right, false) then
                     result := true;
         end;                 
      end; {case}
   OpsEqual := result;
   end; {OpsEqual}


begin {CodesMatch}
CodesMatch := false;
if op1 = op2 then
   CodesMatch := true
else if (op1 <> nil) and (op2 <> nil) then
   if op1^.opcode = op2^.opcode then
      if op1^.q = op2^.q then
         if op1^.r = op2^.r then
            if op1^.s = op2^.s then
               if (op1^.lab = op2^.lab) or (op1^.lab^ = op2^.lab^) then
                  if OpsEqual(op1, op2) then
                     if op1^.optype = op2^.optype then
                        case op1^.optype of
                           cgByte, cgUByte, cgWord, cgUWord:
                              if op1^.opnd = op2^.opnd then
                                 if op1^.llab = op2^.llab then
                                    if op1^.slab = op2^.slab then
                                       CodesMatch := true;
                           cgLong, cgULong:
                              if op1^.lval = op2^.lval then
                                 CodesMatch := true;
                           cgQuad, cgUQuad:
                              if op1^.qval.lo = op2^.qval.lo then
                                 if op1^.qval.hi = op2^.qval.hi then
                                    CodesMatch := true;
                           cgReal, cgDouble, cgComp, cgExtended:
                              if op1^.rval = op2^.rval then
                                 if (SignBit(op1^.rval) = SignBit(op2^.rval))
                                    or fastMath then
                                    CodesMatch := true;
                           cgString:
                              if not (op1^.isByteSeq or op1^.isByteSeq) then
                                 CodesMatch := LongStrCmp(op1^.str, op2^.str);
                           cgVoid, ccPointer:
                              if op1^.pval = op2^.pval then
                                 CodesMatch := LongStrCmp(op1^.str, op2^.str);
                           end; {case}
end; {CodesMatch}

{- Peephole Optimization ---------------------------------------}

function Base (val: longint): integer;

{ Assuming val is a power of 2, find ln(val) base 2		}
{								}
{ parameters:							}
{    val - value for which to find the base			}
{								}
{ Returns: ln(val), base 2					}

var
   i: integer;                    	{base counter}

begin {Base}
i := 0;
while not odd(val) do begin
   val := val >> 1;
   i := i+1;
   end; {while}
Base := i;
end; {Base}


procedure BinOps (var op1, op2: icptr);

{ Make sure the operands are of the same type			}
{								}
{ parameters:							}
{    op1, op2: two pc_ldc operands				}

var
   opt1, opt2: baseTypeEnum;		{temp operand types}

begin {BinOps}
opt1 := op1^.optype;
opt2 := op2^.optype;
if opt1 = cgByte then begin
   op1^.optype := cgWord;
   opt1 := cgWord;
   end {if}
else if opt1 = cgUByte then begin
   op1^.optype := cgWord;
   opt1 := cgWord;
   end {else if}
else if opt1 in [cgReal, cgDouble, cgComp] then begin
   op1^.optype := cgExtended;
   opt1 := cgExtended;
   end; {else if}
if opt2 = cgByte then begin
   op2^.optype := cgWord;
   opt2 := cgWord;
   end {if}
else if opt2 = cgUByte then begin
   op2^.optype := cgWord;
   opt2 := cgWord;
   end {else if}
else if opt2 in [cgReal, cgDouble, cgComp] then begin
   op2^.optype := cgExtended;
   opt2 := cgExtended;
   end; {else if}

if opt1 <> opt2 then begin
   case opt1 of
      cgWord:
         case opt2 of
            cgUWord:
               op1^.optype := cgUWord;
            cgLong, cgULong: begin
               op1^.lval := op1^.q;
               op1^.optype := opt2;
               end;
            cgExtended: begin
               op1^.rval := op1^.q;
               op1^.optype := cgExtended;
               end;
            otherwise: ;
            end; {case}
      cgUWord:
         case opt2 of
            cgWord:
               op2^.optype := cgUWord;
            cgLong, cgULong: begin
               op1^.lval := ord4(op1^.q) & $0000FFFF;
               op1^.optype := opt2;
               end;
            cgExtended: begin
               op1^.rval := ord4(op1^.q) & $0000FFFF;
               op1^.optype := cgExtended;
               end;
            otherwise: ;     
            end; {case}
      cgLong:
         case opt2 of
            cgWord: begin
               op2^.lval := op2^.q;
               op2^.optype := cgLong;
               end;
            cgUWord: begin
               op2^.lval := ord4(op2^.q) & $0000FFFF;
               op2^.optype := cgLong;
               end;
            cgULong:
               op1^.optype := cgULong;
            cgExtended: begin
               op1^.rval := op1^.lval;
               op1^.optype := cgExtended;
               end;
            otherwise: ;
            end; {case}
      cgULong:
         case opt2 of
            cgWord: begin
               op2^.lval := op2^.q;
               op2^.optype := cgLong;
               end;
            cgUWord: begin
               op2^.lval := ord4(op2^.q) & $0000FFFF;
               op2^.optype := cgLong;
               end;
            cgLong:
               op2^.optype := cgULong;
            cgExtended: begin
               op1^.rval := op1^.lval;
               if op1^.rval < 0.0 then
                  op1^.rval := 4294967296.0 + op1^.rval;
               op1^.optype := cgExtended;
               end;
            otherwise: ;
            end; {case}
      cgExtended: begin
         case opt2 of
            cgWord:
               op2^.rval := op2^.q;
            cgUWord:
               op2^.rval := ord4(op2^.q) & $0000FFFF;
            cgLong:
               op2^.rval := op2^.lval;
            cgULong: begin
               op2^.rval := op2^.lval;
               if op2^.rval < 0.0 then
                  op2^.rval := 4294967296.0 + op2^.rval;
               end;
            otherwise: ;
            end; {case}
         op2^.optype := cgExtended;
         end;
      otherwise: ;
      end; {case}
   end; {if}
end; {BinOps}


procedure CheckLabels;

{ remove unused dc_lab labels					}

var
   lop: icptr;				{predecessor of op}
   op: icptr;				{used to trace the opcode list}


   function Used (lab: integer): boolean;

   { see if a label is used					}
   {								}
   { parameters:						}
   {    lab - label number to check				}
   {								}
   { Returns: True if the label is used, else false.		}

   var
      found: boolean;			{was the label found?}
      op: icptr;			{used to trace the opcode list}
   
   begin {Used}
   found := false;
   op := DAGhead;
   while (not found) and (op <> nil) do begin
      if op^.opcode in [pc_add, pc_fjp, pc_tjp, pc_ujp] then
         found := op^.q = lab
      else if op^.opcode = pc_nat then
         found := true;
      op := op^.next;
      end; {while}
   Used := found;
   end; {Used}


begin {CheckLabels}
op := DAGhead;
while op^.next <> nil do begin
   lop := op;
   op := op^.next;
   if op^.opcode = dc_lab then
      if not Used(op^.q) then begin
         lop^.next := op^.next;
         op := lop;
         rescan := true;
         end; {if}
   end; {while}
end; {CheckLabels}


procedure RemoveDeadCode (op: icptr);

{ remove dead code following an unconditional branch		}
{								}
{ parameters:							}
{    op - unconditional branch opcode				}

begin {RemoveDeadCode}
while not (op^.next^.opcode in [dc_lab, dc_enp, dc_cns, dc_glb,
   dc_dst, dc_str, dc_pin, pc_ent, dc_loc, dc_prm, dc_sym]) do begin
   op^.next := op^.next^.next;
   rescan := true;
   end; {while}
end; {RemoveDeadCode}


function OneBit (val: longint): boolean;

{ See if there is exactly one bit set in val			}
{								}
{ parameters:							}
{    val - value to check					}
{								}
{ Returns: True if exactly one bit is set, else false		}

begin {OneBit}
if val = 0 then
   OneBit := false
else begin
   while not odd(val) do
      val := val >> 1;
   OneBit := val = 1;
   end; {else}
end; {OneBit}


procedure PeepHoleOptimization (var opv: icptr);

{ do peephole optimization on a list of opcodes			}
{								}
{ parameters:							}
{    opv - pointer to the first opcode				}
{								}
{ Notes:							}
{    1.	Many optimizations assume the children have already	}
{	been optimized.  In particular, many optimizations	}
{	depend on pc_ldc operands being on a specific side of	}
{	a child's expression tree.  (e.g. pc_fjp and pc_equ)	}

var
   done: boolean;			{optimization done test}
   doit: boolean;			{should we do the optimization?}
   lq, lval: longint;			{temps for long calculations}
   op2,op3: icptr;			{temp opcodes}
   op: icptr;				{copy of op (for efficiency)}
   opcode: pcodes;			{temp opcode}
   optype: baseTypeEnum;		{temp optype}
   q: integer;				{temp for integer calculations}
   rval: extended;			{temp for real calculations}

   fromtype, totype, firstType: record	{for converting numbers to optypes}
      case boolean of
         true:  (i: integer);
         false: (optype: baseTypeEnum);
      end;


   function SideEffects (op: icptr): boolean;

   { Check a tree for operations that have side effects	}
   {								}
   { parameters:						}
   {    op - tree to check					}

   begin {SideEffects}
   if op = nil then begin
      if volatile then
         SideEffects := true
      else
         SideEffects := false
      end {if}
   else if op^.opcode in
      [pc_mov,pc_cbf,pc_cop,pc_cpi,pc_cpo,pc_gil,pc_gli,pc_gdl,
       pc_gld,pc_iil,pc_ili,pc_idl,pc_ild,pc_lil,pc_lli,pc_ldl,
       pc_lld,pc_sbf,pc_sro,pc_sto,pc_str,pc_cui,pc_cup,pc_tl1,
       pc_fix,pc_ckp] then
      SideEffects := true
   else if op^.opcode = pc_ldc then
      SideEffects := false
   else
      SideEffects := SideEffects(op^.left) or SideEffects(op^.right);
   end; {SideEffects}


   procedure JumpOptimizations (op: icptr; newOpcode: pcodes);

   { handle common code for jump optimizations			}
   {								}
   { parameters:						}
   {    op - jump opcode					}
   {    newOpcode - opcode to use if the jump sense is reversed	}

   var
      topcode: pcodes;			{temp opcode}

   begin {JumpOptimizations}
   topcode := op^.left^.opcode;
   if topcode = pc_not then begin
      op^.left := op^.left^.left;
      op^.opcode := newOpcode;
      PeepHoleOptimization(opv);
      end {else if}
   else if topcode in [pc_neq,pc_equ] then begin
      with op^.left^.right^ do
         if opcode = pc_ldc then
            if optype in [cgByte,cgUByte,cgWord,cgUWord] then
               if q = 0 then begin
                  op^.left := op^.left^.left;
                  if topcode = pc_equ then
                     op^.opcode := newOpcode;
                  end; {if}
      end; {else if}
   if op^.next^.opcode = dc_lab then
      if op^.next^.q = op^.q then
         if not SideEffects(op^.left) then begin
            rescan := true;
            opv := op^.next;
            end; {else if}
   end; {JumpOptimizations}


   procedure RealStoreOptimizations (op, opl: icptr);

   { do strength reductions associated with stores of reals	}
   {								}
   { parameters:						}
   {    op - real store to optimize				}
   {    opl - load operand for the store operation		}

   var
      disp: 0..9;			{disp to the word to change}
      same: boolean;			{are the operands the same?}
      op2: icptr;			{new opcode}
      opt: icptr;			{temp opcode}

      cnvrl: record			{for stuffing a real in a long space}
	 case boolean of
            true:  (lval: longint);
            false: (rval: real);
	 end;

      cnvdbl: record			{for stuffing a double in a quad space}
         case boolean of
            true:  (qval: longlong);
            false: (rval: double);
         end;

   begin {RealStoreOptimizations}
   if opl^.opcode = pc_cnv then
      if baseTypeEnum(opl^.q & $000F) = op^.optype then
         opl^.q := (opl^.q & $FFF0) | ord(cgExtended);
   if (op^.optype = cgComp) or not (op^.opcode in [pc_sro,pc_str,pc_sto]) then
      {skip below optimizations}
   else if opl^.opcode = pc_ngr then begin
      same := false;
      with opl^.left^ do
         if op^.opcode = pc_sro then begin
            if opcode = pc_ldo then
               if q = op^.q then
                  if optype = op^.optype then
                     if lab^ = op^.lab^ then
                        same := true;
            end {if}
         else if op^.opcode = pc_str then
            if opcode = pc_lod then
               if q = op^.q then
                  if r = op^.r then
                     if optype = op^.optype then
                        same := true;
      if same then begin
         case op^.optype of
            cgReal: disp := 2;
            cgDouble: disp := 6;
            cgExtended: disp := 8;
            end; {case}
         opl^.left^.optype := cgWord;
         opl^.left^.q := opl^.left^.q + disp;
         op^.optype := cgWord;
         op^.q := op^.q + disp;
         op2 := pointer(Calloc(sizeof(intermediate_code)));
         op2^.opcode := pc_ldc;
         op2^.optype := cgWord;
         op2^.q := $8000;
         opl^.right := op2;
         opl^.opcode := pc_bxr;
         end {if}
      else if op^.optype = cgReal then begin
         opt := opl^.left;
         if opt^.opcode in [pc_ind,pc_ldo,pc_lod] then
            if opt^.optype = cgReal then begin
               opt^.optype := cgLong;
               op^.optype := cgLong;
               op2 := pointer(Calloc(sizeof(intermediate_code)));
               op2^.opcode := pc_ldc;
               op2^.optype := cgLong;
               op2^.lval := $80000000;
	       opl^.right := op2;
               opl^.opcode := pc_blx;
               end; {if}
         end; {else if}
      end {if}
   else if op^.optype = cgReal then begin
      if opl^.opcode = pc_ldc then begin
         cnvrl.rval := opl^.rval;
         opl^.lval := cnvrl.lval;
         opl^.optype := cgLong;
         op^.optype := cgLong;
         end {if}
      else if opl^.opcode in [pc_ind,pc_ldo,pc_lod] then
         if opl^.optype = cgReal then begin
            opl^.optype := cgLong;
            op^.optype := cgLong;
            end; {if}
      end {if}
   else if op^.optype = cgDouble then begin
      if opl^.opcode = pc_ldc then begin
         cnvdbl.rval := opl^.rval;
         opl^.qval := cnvdbl.qval;
         opl^.optype := cgQuad;
         op^.optype := cgQuad;
         end {if}
      else if opl^.opcode in [pc_ind,pc_ldo,pc_lod] then
         if opl^.optype = cgDouble then begin
            opl^.optype := cgQuad;
            op^.optype := cgQuad;
            end; {if}
      end; {if}
   end; {RealStoreOptimizations}


   procedure ReplaceLoads (ldop, stop, tree: icptr);

   { Replace any pc_lod operations in tree that load from the	}
   { location stored to by the pc_str operation stop by ldop	}
   {								}
   { parameters:						}
   {    ldop - operation to replace the pc_lods with		}
   {    stop - pc_str operation					}
   {    tree - tree to check for pc_lod operations		}
   {								}
   { Notes: ldop must be an instruction, not a tree		}

   begin {ReplaceLoads}
   if tree^.left <> nil then
      ReplaceLoads(ldop, stop, tree^.left);
   if tree^.right <> nil then
      ReplaceLoads(ldop, stop, tree^.right);
   if tree^.opcode = pc_lod then
      if tree^.optype = stop^.optype then
         if tree^.q = stop^.q then
            if tree^.r = stop^.r then
               tree^ := ldop^;
   end; {ReplaceLoads}


   procedure ReverseChildren (op: icptr);

   { reverse the children of a node				}
   {								}
   { parameters:						}
   {    op - node for which to reverse the children		}

   var
      opt: icptr;			{temp opcode pointer}

   begin {ReverseChildren}
   opt := op^.right;
   op^.right := op^.left;
   op^.left := opt;
   end; {ReverseChildren}


   procedure ZeroIntermediateCode (op: icptr);

   { Set all fields in the record to 0, nil, etc.		}
   {								}
   { Parameters:						}
   {    op - intermediate code record to clear			}

   begin {ZeroIntermediateCode}
   op^.q := 0;
   op^.r := 0;
   op^.s := 0;
   op^.lab := nil;
   op^.next := nil;
   op^.left := nil;
   op^.right := nil;
   op^.optype := cgWord;
   op^.opnd := 0;
   op^.llab := 0;
   op^.slab := 0;
   end; {ZeroIntermediateCode}


begin {PeepHoleOptimization}
{if printSymbols then begin write('Optimize: '); WriteCode(opv); end; {debug}
op := opv;				{copy for efficiency}
if op^.left <> nil then			{optimize the children}
   PeepHoleOptimization(op^.left);
if op^.right <> nil then
   PeepHoleOptimization(op^.right);
case op^.opcode of			{check for optimizations of this node}
   pc_add: begin			{pc_add}
      if op^.next^.opcode <> pc_add then
         RemoveDeadCode(op);
      end; {case pc_add}

   pc_adi: begin			{pc_adi}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         op^.left^.q := op^.left^.q + op^.right^.q;
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            q := op^.right^.q;
            if q = 0 then
               opv := op^.left
            else if q > 0 then begin
               op^.opcode := pc_inc;
               op^.q := q;
               op^.right := nil;
               PeepHoleOptimization(opv);
               end {else if}
            else {if q < 0 then} begin
               op^.opcode := pc_dec;
               op^.q := -q;
               op^.right := nil;
               PeepHoleOptimization(opv);
               end; {else if}
            end {if}
         else if CodesMatch(op^.left, op^.right, false) then begin
            if not SideEffects(op^.left) then begin
               ZeroIntermediateCode(op^.right);
               with op^.right^ do begin
                  opcode := pc_ldc;
                  q := 1;
                  optype := cgWord;
                  end; {with}
               op^.opcode := pc_shl;
               PeepHoleOptimization(opv);
               end; {if}
            end {else if}
         else if op^.left^.opcode in [pc_inc,pc_dec] then begin
            if op^.right^.opcode in [pc_inc,pc_dec] then begin
               op2 := op^.left;
               if op2^.opcode = pc_inc then
                  q := op2^.q
               else
                  q := -op2^.q;
               if op^.right^.opcode = pc_inc then
                  q := q + op^.right^.q
               else
                  q := q - op^.right^.q;
               if q >= 0 then begin
                  op2^.opcode := pc_inc;
                  op2^.q := q;
                  end {if}
               else begin
                  op2^.opcode := pc_dec;
                  op2^.q := -q;
                  end; {else}
               op^.left := op^.left^.left;
               op^.right := op^.right^.left;
               op2^.left := op;
               opv := op2;
               PeepHoleOptimization(opv);
               end; {if}
            end; {else if}
         end; {else}
      end; {case pc_adi}

   pc_adl: begin			{pc_adl}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         op^.left^.lval := op^.left^.lval + op^.right^.lval;
         opv := op^.left;
         end {if}
      else begin
         done := false;
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            lval := op^.right^.lval;
            if lval = 0 then begin
               opv := op^.left;
               done := true;
               end {if}
            else if (lval >= 0) and (lval <= maxint) then begin
               op^.opcode := pc_inc;
               op^.optype := cgLong;
               op^.q := ord(lval);
               op^.right := nil;
               done := true;
               PeepHoleOptimization(opv);
               end {else if}
            else if (lval > -maxint) and (lval < 0) then begin
               op^.opcode := pc_dec;
               op^.optype := cgLong;
               op^.q := -ord(lval); 
               op^.right := nil;
               done := true;
               PeepHoleOptimization(opv);
               end; {else if}
            end {if}
         else if CodesMatch(op^.left, op^.right, false) then
            if not SideEffects(op^.left) then begin
               ZeroIntermediateCode(op^.right);
               with op^.right^ do begin
                  opcode := pc_ldc;
                  lval := 1;
                  optype := cgLong;
                  end; {with}
               op^.opcode := pc_sll;
               done := true;
               end; {if}
         if not done and (op^.right^.opcode in [pc_lao,pc_lda,pc_ixa]) then
            ReverseChildren(op);
         if not done and (op^.left^.opcode in [pc_lao,pc_lda,pc_ixa]) then
            if op^.right^.opcode = pc_sll then begin
               if op^.right^.right^.opcode = pc_ldc then
                  if (op^.right^.right^.lval & $FFFF8000) = 0 then
                     if op^.right^.left^.opcode = pc_cnv then begin
                	fromtype.i := (op^.right^.left^.q & $00F0) >> 4;
                	if fromType.optype in [cgByte,cgUByte,cgWord,cgUWord] then
                           if op^.left^.opcode = pc_lda then
                              begin
                              if fromType.optype = cgByte then
                                 op^.right^.left^.q := $02
                              else if fromType.optype = cgUByte then
                                 op^.right^.left^.q := $13
                              else
                                 op^.right^.left := op^.right^.left^.left;
                              with op^.right^.right^ do begin
                                 lq := lval;
                                 lval := 0;
                                 q := long(lq).lsw;
                                 optype := cgUWord;
                                 end; {with}
                              op^.right^.opcode := pc_shl;
                              op^.opcode := pc_ixa;
                              if fromType.optype in [cgByte,cgWord] then
                                 op^.optype := cgWord
                              else
                                 op^.optype := cgUWord;
                              PeepHoleOptimization(opv);
                              end; {if}
                	end; {if}
               end {if}
            else if op^.right^.opcode = pc_cnv then begin
               fromtype.i := (op^.right^.q & $00F0) >> 4;
               if fromtype.optype in [cgByte,cgUByte,cgWord,cgUWord] then begin
                  if fromType.optype = cgByte then
                     op^.right^.q := $02
                  else if fromType.optype = cgUByte then
                     op^.right^.q := $13
                  else
                     op^.right := op^.right^.left;
                  op^.opcode := pc_ixa;
                  if fromType.optype in [cgByte,cgWord] then
                     op^.optype := cgWord
                  else
                     op^.optype := cgUWord;
                  PeepHoleOptimization(opv);
                  end; {if}
               end; {else if}
         end; {else}
      end; {case pc_adl}

   pc_adr: begin			{pc_adr}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         op^.left^.rval := op^.left^.rval + op^.right^.rval;
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            if fastMath then
               if op^.right^.rval = 0.0 then
                  opv := op^.left;
            end; {if}
         end; {else}   
      end; {case pc_adr}

   pc_adq: begin			{pc_adq}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         add64(op^.left^.qval, op^.right^.qval);
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            if (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0) then
               opv := op^.left;
            end; {if}
         end; {else}
      end; {case pc_adq}

   pc_and: begin			{pc_and}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.q := ord((op^.left^.q <> 0) and (op^.right^.q <> 0));
            opv := op^.left;
            end {if}
         else begin
            if op^.right^.q = 0 then 
               if not SideEffects(op^.left) then 
                  opv := op^.right;
            end {else}
         end {if}
      else if op^.left^.opcode = pc_ldc then
         if op^.left^.q = 0 then
            opv := op^.left;
      end; {case pc_and}

   pc_bal: begin			{pc_bal}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.lval := op^.left^.lval & op^.right^.lval;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.lval = 0 then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if op^.right^.lval = -1 then
            opv := op^.left;
         end; {else if}
      end; {case pc_bal}

   pc_baq: begin			{pc_baq}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.qval.hi := op^.left^.qval.hi & op^.right^.qval.hi;
         op^.left^.qval.lo := op^.left^.qval.lo & op^.right^.qval.lo;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0) then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if (op^.right^.qval.lo = -1) and (op^.right^.qval.hi = -1) then
            opv := op^.left;
         end; {else if}
      end; {case pc_baq}

   pc_blr: begin			{pc_blr}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.lval := op^.left^.lval | op^.right^.lval;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.lval = -1 then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if op^.right^.lval = 0 then
            opv := op^.left;
         end; {else if}
      end; {case pc_blr}

   pc_bqr: begin			{pc_bqr}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.qval.hi := op^.left^.qval.hi | op^.right^.qval.hi;
         op^.left^.qval.lo := op^.left^.qval.lo | op^.right^.qval.lo;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if (op^.right^.qval.hi = -1) and (op^.right^.qval.lo = -1) then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if (op^.right^.qval.hi = 0) and (op^.right^.qval.lo = 0) then
            opv := op^.left;
         end; {else if}
      end; {case pc_bqr}

   pc_blx: begin			{pc_blx}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.lval := op^.left^.lval ! op^.right^.lval;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.lval = 0 then
            opv := op^.left
         else if op^.right^.lval = -1 then begin
            op^.opcode := pc_bnl;
            op^.right := nil;
            end; {else if}
         end; {else if}
      end; {case pc_blx}

   pc_bqx: begin			{pc_bqx}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.qval.hi := op^.left^.qval.hi ! op^.right^.qval.hi;
         op^.left^.qval.lo := op^.left^.qval.lo ! op^.right^.qval.lo;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0) then
            opv := op^.left
         else if (op^.right^.qval.lo = -1) and (op^.right^.qval.hi = -1) then begin
            op^.opcode := pc_bnq;
            op^.right := nil;
            end; {else if}
         end; {else if}
      end; {case pc_bqx}

   pc_bnd: begin			{pc_bnd}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.q := op^.left^.q & op^.right^.q;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.q = 0 then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if op^.right^.q = -1 then
            opv := op^.left;
         end; {else if}
      end; {case pc_bnd}

   pc_bnl: begin			{pc_bnl}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.lval := op^.left^.lval ! $FFFFFFFF;
         opv := op^.left;
         end; {if}
      end; {case pc_bnl}

   pc_bnq: begin			{pc_bnq}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.qval.hi := op^.left^.qval.hi ! $FFFFFFFF;
         op^.left^.qval.lo := op^.left^.qval.lo ! $FFFFFFFF;
         opv := op^.left;
         end; {if}
      end; {case pc_bnq}

   pc_bno: begin			{pc_bno}
      {Invalid optimization disabled}
      {if op^.left^.opcode = pc_str then
         if op^.left^.left^.opcode in [pc_lda,pc_lao] then begin
            ReplaceLoads(op^.left^.left, op^.left, op^.right);
            opv := op^.right;
            end;} {if}
      end; {case pc_bno}

   pc_bnt: begin			{pc_bnt}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.q := op^.left^.q ! $FFFF;
         opv := op^.left;
         end; {if}
      end; {case pc_bnt}

   pc_bor: begin			{pc_bor}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.q := op^.left^.q | op^.right^.q;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.q = -1 then begin
            if not SideEffects(op^.left) then
               opv := op^.right;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end {else if}
      else if ((op^.left^.opcode = pc_shl) and (op^.right^.opcode = pc_usr))
         or ((op^.left^.opcode = pc_usr) and (op^.right^.opcode = pc_shl)) then
         if op^.left^.right^.opcode = pc_ldc then
            if op^.right^.right^.opcode = pc_ldc then
               if op^.left^.right^.q = 8 then
                  if op^.right^.right^.q = 8 then
                     if CodesMatch(op^.left^.left, op^.right^.left, false) then
                        if not SideEffects(op^.left^.left) then begin
                           op^.opcode := pc_rbo;
                           op^.left := op^.left^.left;
                           op^.right := nil;
                           end; {if}
         
      end; {case pc_bor}

   pc_bxr: begin			{pc_bxr}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.q := op^.left^.q ! op^.right^.q;
         opv := op^.left;
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if op^.right^.q = 0 then
            opv := op^.left
         else if op^.right^.q = -1 then begin
            op^.opcode := pc_bnt;
            op^.right := nil;
            end; {else if}
         end; {else if}
      end; {case pc_bxr}

   pc_cnv: begin			{pc_cnv}
      fromtype.i := (op^.q & $00F0) >> 4;
      totype.i := op^.q & $000F;
      if (fromtype.optype = cgWord) and (TypeOf(op^.left) = cgUByte) then begin
         fromType.optype := cgUWord;
         op^.q := (op^.q & $FF0F) | (fromtype.i << 4);
         end; {if}
      if op^.left^.opcode = pc_ldc then begin
         doit := true;
	 case fromtype.optype of
            cgByte,cgWord:
               case totype.optype of
        	  cgByte,cgUByte,cgWord,cgUWord: ;
        	  cgLong,cgULong: begin
                     lval := op^.left^.q;
                     op^.left^.q := 0;
                     op^.left^.lval := lval;
                     end;
        	  cgQuad,cgUQuad: begin
                     op^.left^.qval.lo := op^.left^.q;
                     if op^.left^.qval.lo < 0 then
                        op^.left^.qval.hi := -1
                     else
                        op^.left^.qval.hi := 0;
                     op^.left^.q := 0;
                     end;
        	  cgReal,cgDouble,cgComp,cgExtended: begin
                     rval := op^.left^.q;
                     LimitPrecision(rval, totype.optype);
                     op^.left^.q := 0;
                     op^.left^.rval := rval;
                     end;
                  otherwise: ;
        	  end; {case}                             
            cgUByte,cgUWord:
               case totype.optype of
        	  cgByte,cgUByte,cgWord,cgUWord: ;
        	  cgLong,cgULong: begin
                     lval := ord4(op^.left^.q) & $0000FFFF;
                     op^.left^.q := 0;
                     op^.left^.lval := lval;
                     end;
        	  cgQuad,cgUQuad: begin
                     op^.left^.qval.lo := ord4(op^.left^.q) & $0000FFFF;
                     op^.left^.qval.hi := 0;
                     op^.left^.q := 0;
                     end;
        	  cgReal,cgDouble,cgComp,cgExtended: begin
                     rval := ord4(op^.left^.q) & $0000FFFF;
                     LimitPrecision(rval, totype.optype);
                     op^.left^.q := 0;
                     op^.left^.rval := rval;
                     end;
                  otherwise: ;
        	  end; {case}                             
            cgLong:
               case totype.optype of
        	  cgByte,cgUByte,cgWord,cgUWord: begin
                     q := long(op^.left^.lval).lsw;                
                     op^.left^.lval := 0;
                     op^.left^.q := q;
                     end;
        	  cgLong, cgULong: ;
        	  cgQuad,cgUQuad: begin
                     op^.left^.qval.lo := op^.left^.lval;
                     if op^.left^.qval.lo < 0 then
                        op^.left^.qval.hi := -1
                     else
                        op^.left^.qval.hi := 0;
                     end;
        	  cgReal,cgDouble,cgComp,cgExtended: begin
                     rval := op^.left^.lval;
                     LimitPrecision(rval, totype.optype);
                     op^.left^.lval := 0;
                     op^.left^.rval := rval;
                     end;
                  otherwise: ;
        	  end; {case}
            cgULong:
               case totype.optype of
        	  cgByte,cgUByte,cgWord,cgUWord: begin
                     q := long(op^.left^.lval).lsw;
                     op^.left^.lval := 0;
                     op^.left^.q := q;
                     end;
        	  cgLong, cgULong: ;
        	  cgQuad,cgUQuad: begin
                     op^.left^.qval.lo := op^.left^.lval;
                     op^.left^.qval.hi := 0;
                     end;
        	  cgReal,cgDouble,cgComp,cgExtended: begin
                     lval := op^.left^.lval;
                     op^.left^.lval := 0;
                     if lval >= 0 then
                        rval := lval
                     else
                        rval := (lval & $7FFFFFFF) + 2147483648.0;
                     LimitPrecision(rval, totype.optype);
                     op^.left^.rval := rval;
                     end;
                  otherwise: ;
        	  end; {case}
            cgQuad:
               case totype.optype of
                  cgByte,cgUByte,cgWord,cgUWord: begin
                     q := long(op^.left^.qval.lo).lsw;
                     op^.left^.qval := longlong0;
                     op^.left^.q := q;
                     end;
                  cgLong, cgULong: begin
                     lval := op^.left^.qval.lo;
                     op^.left^.qval := longlong0;
                     op^.left^.lval := lval;
                     end;
                  cgQuad,cgUQuad: ;
                  cgDouble,cgExtended: begin
                     rval := CnvLLX(op^.left^.qval);
                     LimitPrecision(rval, totype.optype);
                     op^.left^.qval := longlong0;
                     op^.left^.rval := rval;
                     end;
                  cgReal,cgComp:
                     doit := false;
                  otherwise: ;
                  end; {case}
            cgUQuad:
               case totype.optype of
                  cgByte,cgUByte,cgWord,cgUWord: begin
                     q := long(op^.left^.qval.lo).lsw;
                     op^.left^.qval := longlong0;
                     op^.left^.q := q;
                     end;
                  cgLong, cgULong: begin
                     lval := op^.left^.qval.lo;
                     op^.left^.qval := longlong0;
                     op^.left^.lval := lval;
                     end;
                  cgQuad,cgUQuad: ;
                  cgDouble,cgExtended: begin
                     rval := CnvULLX(op^.left^.qval);
                     LimitPrecision(rval, totype.optype);
                     op^.left^.qval := longlong0;
                     op^.left^.rval := rval;
                     end;
                  cgReal,cgComp:
                     doit := false;
                  otherwise: ;
                  end; {case}
            cgReal,cgDouble,cgComp,cgExtended: begin
               rval := op^.left^.rval;
               case totype.optype of
        	  cgByte: begin
                     if rval < -128.0 then
                        q := -128
                     else if rval > 127.0 then
                        q := 127
                     else
                        q := trunc(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.q := q;
                     end;
        	  cgUByte: begin
                     if rval < 0.0 then
                        q := 0
                     else if rval > 255.0 then
                        q := 255
                     else
                        q := trunc(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.q := q;
                     end;
        	  cgWord: begin
                     if rval < -32768.0 then
                        lval := -32768
                     else if rval > 32767.0 then
                        lval := 32767
                     else
                        lval := trunc(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.q := long(lval).lsw;
                     end;
        	  cgUWord: begin
                     if rval < 0.0 then
                        lval := 0
                     else if rval > 65535.0 then
                        lval := 65535
                     else
                        lval := trunc4(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.q := long(lval).lsw;
                     end;
        	  cgLong: begin
                     if rval < -2147483648.0 then
                        lval := $80000000
                     else if rval > 2147483647.0 then
                        lval := 2147483647
                     else
                        lval := trunc4(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.lval := lval;
                     end;
        	  cgULong: begin
                     if rval < 0.0 then
                        lval := 0
                     else if rval >= 4294967295.0 then
                        lval := $FFFFFFFF
                     else if rval > 2147483647.0 then begin
                        rval := rval - 2147483647.0;
                        lval := 2147483647 + trunc4(rval);
                        end {else if}
                     else
                        lval := trunc4(rval);
                     op^.left^.rval := 0.0;
                     op^.left^.lval := lval;
                     end;
                  cgQuad:
                     CnvXLL(op^.left^.qval, rval);
                  cgUQuad:
                     CnvXULL(op^.left^.qval, rval);
        	  cgReal,cgDouble,cgComp,cgExtended:
        	     LimitPrecision(rval, totype.optype);
                  otherwise: ;
                  end;
               end; {case}        
            otherwise: ;
            end; {case}
         if doit then
            if fromtype.optype in
               [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong,cgQuad,cgUQuad,
               cgReal,cgDouble,cgComp,cgExtended] then
               if totype.optype in
                  [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong,cgQuad,cgUQuad,
                  cgReal,cgDouble,cgComp,cgExtended] then begin
                  op^.left^.optype := totype.optype;
                  if totype.optype in [cgByte,cgUByte] then begin
                     op^.left^.q := op^.left^.q & $00FF;
                     if totype.optype = cgByte then
                        if (op^.left^.q & $0080) <> 0 then
                           op^.left^.q := op^.left^.q | $FF00;
                     end; {if}
                  opv := op^.left;
                  end; {if}
         end {if}
      else if op^.left^.opcode = pc_cnv then begin
         doit := false;
         firsttype.i := (op^.left^.q & $00F0) >> 4;
         if fromType.optype in [cgReal,cgDouble,cgComp,cgExtended] then begin
            if toType.optype in [cgReal,cgDouble,cgComp,cgExtended] then
               if (baseTypeEnum(op^.left^.q & $000F) = toType.optype)
                  or (baseTypeEnum(op^.left^.q & $000F) = cgExtended) then
                  doit := true;
            end {if}
         else begin
            if firstType.optype in [cgByte,cgWord,cgLong] then
               if fromType.optype in [cgByte,cgWord,cgLong] then
                  if toType.optype in [cgByte,cgWord,cgLong] then
                     doit := true;
            if firstType.optype in [cgUByte,cgUWord,cgULong] then
               if fromType.optype in [cgUByte,cgUWord,cgULong] then
                  if toType.optype in [cgUByte,cgUWord,cgLong] then
                     doit := true;
            if TypeSize(firstType.optype) = TypeSize(fromType.optype) then
               if TypeSize(firstType.optype) = TypeSize(toType.optype) then
                  doit := true;
            if TypeSize(fromType.optype) < TypeSize(firstType.optype) then
               if TypeSize(fromType.optype) < TypeSize(toType.optype) then
                  doit := false;        {disable optimization in invalid cases}
            end; {else}
         if doit then begin
            op^.q := (op^.left^.q & $00F0) | (op^.q & $000F);
            op^.left := op^.left^.left;
            PeepHoleOptimization(opv);
            end; {if}
         end {else if}
      else if (op^.left^.opcode in [pc_lod,pc_ldo]) or
         ((op^.left^.opcode = pc_ind) and (op^.left^.r = 0)) then begin
         if fromtype.optype in [cgWord,cgUWord] then
            if totype.optype in [cgByte,cgUByte,cgWord,cgUWord] then begin
               op^.left^.optype := totype.optype;
               opv := op^.left;
               end; {if}
         if fromtype.optype in [cgLong,cgULong] then
            if totype.optype in [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong]
               then begin
               op^.left^.optype := totype.optype;
               opv := op^.left;
               end; {if}
         if fromtype.optype in [cgQuad,cgUQuad] then
            if totype.optype in
               [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong,cgQuad,cgUQuad]
               then begin
               op^.left^.optype := totype.optype;
               opv := op^.left;
               end; {if}
         if fromtype.optype in [cgReal,cgDouble,cgExtended,cgComp] then
            if totype.optype in [cgReal,cgDouble,cgExtended,cgComp] then
               if (totype.optype = op^.left^.optype) or
                  (totype.optype = cgExtended) or
                  ((totype.optype = cgDouble) and (op^.left^.optype = cgReal)) then
                  opv := op^.left;
         end {else if}
      else if op^.q in [$40,$41,$50,$51] then begin
         {any long type to byte type}
         with op^.left^ do
            if opcode = pc_bal then
               if right^.opcode = pc_ldc then
                  if right^.lval = 255 then begin
                     op^.left := op^.left^.left;
                     PeepHoleOptimization(opv);
                     end; {if}
         with op^.left^ do    
            if opcode in [pc_slr,pc_vsr] then
               if right^.opcode = pc_ldc then
                  if (left^.opcode in [pc_lod,pc_ldo]) or
                     ((left^.opcode = pc_ind) and (left^.r = 0)) then begin
                     lq := right^.lval;
                     if long(lq).msw = 0 then
                        if long(lq).lsw in [8,16,24] then begin
                           lq := lq div 8;
                           left^.q := left^.q + long(lq).lsw;
                           op^.left := left;
                	   PeepHoleOptimization(opv);
                	   end; {if}
                     end; {if}
         end; {else if}                     
      end; {case pc_cnv}

   pc_cop,pc_cpo: begin			{pc_cop,pc_cpo}
      if op^.optype in [cgReal,cgDouble,cgExtended,cgComp] then
         RealStoreOptimizations(op, op^.left);
      end; {case pc_cop,pc_cpo}

   pc_cpi: begin			{pc_cpi}
      if op^.optype in [cgReal,cgDouble,cgExtended,cgComp] then
         RealStoreOptimizations(op, op^.right);
      end; {case pc_cpi}

   pc_dec: begin			{pc_dec}
      if op^.q = 0 then
         opv := op^.left
      else begin
	 opcode := op^.left^.opcode;
	 if opcode = pc_dec then begin
            if ord4(op^.left^.q) + ord4(op^.q) < ord4(maxint) then begin
               op^.q := op^.q + op^.left^.q;
               op^.left := op^.left^.left;
               end; {if}
            end {if}
	 else if opcode = pc_inc then begin
            q := op^.q - op^.left^.q;
            if q < 0 then begin
               q := -q;
               op^.opcode := pc_inc;
               end; {if}
            op^.q := q;
            op^.left := op^.left^.left;
            PeepHoleOptimization(opv);
            end {else if}
         else if opcode = pc_ldc then begin
            if op^.optype in [cgLong, cgULong] then begin
               op^.left^.lval := op^.left^.lval - op^.q;
               opv := op^.left;
               end {if}
            else if op^.optype in [cgUByte, cgByte, cgUWord, cgWord] then begin
               op^.left^.q := op^.left^.q - op^.q;
               opv := op^.left;
               end; {else if}
            end; {else if}
         end; {else}
      end; {case pc_dec}

   pc_dvi: begin			{pc_dvi}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            if op^.right^.q <> 0 then begin
               op^.left^.q := op^.left^.q div op^.right^.q;
               opv := op^.left;
               end; {if}
            end {if}
         else if op^.right^.q = 1 then
            opv := op^.left;
         end; {if}
      end; {case pc_dvi}

   pc_dvl: begin			{pc_dvl}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            if op^.right^.lval <> 0 then begin
               op^.left^.lval := op^.left^.lval div op^.right^.lval;
               opv := op^.left;
               end; {if}
            end {if}
         else if op^.right^.lval = 1 then
            opv := op^.left;
         end; {if}
      end; {case pc_dvl}

   pc_dvq: begin			{pc_dvq}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            if (op^.right^.qval.lo <> 0) or (op^.right^.qval.hi <> 0) then begin
               div64(op^.left^.qval, op^.right^.qval);
               opv := op^.left;
               end; {if}
            end {if}
         else if (op^.right^.qval.lo = 1) and (op^.right^.qval.hi = 0) then
            opv := op^.left;
         end; {if}
      end; {case pc_dvq}

   pc_dvr: begin			{pc_dvr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            if op^.right^.rval <> 0.0 then begin
               op^.left^.rval := op^.left^.rval/op^.right^.rval;
               opv := op^.left;
               end; {if}
            end {if}
         else if op^.right^.rval = 1.0 then
            opv := op^.left;
         end; {if}
      end; {case pc_dvr}

   pc_equ: begin			{pc_equ}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.right^.opcode = pc_ldc then begin
	 if op^.left^.opcode = pc_ldc then begin
            BinOps(op^.left, op^.right);
            case op^.left^.optype of
               cgByte,cgUByte,cgWord,cgUWord: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.q = op^.right^.q);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgLong,cgULong: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.lval = op^.right^.lval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgQuad,cgUQuad: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord((op^.left^.qval.lo = op^.right^.qval.lo) and
        	               (op^.left^.qval.hi = op^.right^.qval.hi));
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgReal,cgDouble,cgComp,cgExtended: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.rval = op^.right^.rval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgVoid,ccPointer: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.pval = op^.right^.pval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               end; {case}
            op^.optype := cgWord;
            end {if}
         else if op^.right^.optype in [cgByte, cgUByte, cgWord, cgUWord] then begin
            if op^.right^.q = 1 then
               if op^.left^.opcode in
         	  [pc_and,pc_ior,pc_neq,pc_equ,pc_geq,pc_leq,pc_les,pc_grt,pc_not]
                  then begin
                  opv := op^.left;
                  opv^.next := op^.next;
                  end; {if}
            end {else if}
         else if op^.right^.optype in [cgLong, cgULong] then begin
            if op^.right^.lval = 1 then
               if op^.left^.opcode in
         	  [pc_and,pc_ior,pc_neq,pc_equ,pc_geq,pc_leq,pc_les,pc_grt,pc_not]
                  then begin
                  opv := op^.left;
                  opv^.next := op^.next;
                  end; {if}
            end; {else if}
         end; {if}                      
      end; {case pc_equ}

   pc_fjp: begin			{pc_fjp}
      opcode := op^.left^.opcode;
      if opcode = pc_ldc then begin
         if op^.left^.optype in [cgByte, cgUByte, cgWord, cgUWord] then begin
            if op^.left^.q <> 0 then begin
               opv := op^.next;
               rescan := true;
               end {if}
            else begin
               op^.opcode := pc_ujp;
               op^.left := nil;
               PeepHoleOptimization(opv);
               end; {else}
            end {if}
         end {if}
      else if opcode = pc_and then begin
         op2 := op^.left;
         op2^.next := op^.next;
         op^.next := op2;
         op^.left := op2^.left;
         op2^.left := op2^.right;
         op2^.right := nil;
         op2^.opcode := pc_fjp;
         op2^.q := op^.q;
         PeepHoleOptimization(opv);
         end {else if}
      else if opcode = pc_ior then begin
         op2 := op^.left;
         op2^.next := op^.next;
         op^.next := op2;
         op^.left := op2^.left;
         op2^.left := op2^.right;
         op2^.right := nil;
         op2^.opcode := pc_fjp;
         op2^.q := op^.q;
         op^.opcode := pc_tjp;
         op3 := pointer(Calloc(sizeof(intermediate_code)));
         op3^.opcode := dc_lab;
         op3^.optype := cgWord;
         op3^.q := GenLabel;
         op3^.next := op2^.next;
         op2^.next := op3;
         op^.q := op3^.q;
         PeepHoleOptimization(opv);
         end {else if}
      else
         JumpOptimizations(op, pc_tjp);
      end; {case pc_fjp}

   pc_inc: begin			{pc_inc}
      if op^.q = 0 then
         opv := op^.left
      else begin
	 opcode := op^.left^.opcode;
	 if opcode = pc_inc then begin
            if ord4(op^.left^.q) + ord4(op^.q) < ord4(maxint) then begin
               op^.q := op^.q + op^.left^.q;
               op^.left := op^.left^.left;
               end; {if}
            end {if}
	 else if opcode = pc_dec then begin
            q := op^.q - op^.left^.q;
            if q < 0 then begin
               q := -q;
               op^.opcode := pc_dec;
               end; {if}
            op^.q := q;
            op^.left := op^.left^.left;
            PeepHoleOptimization(opv);
            end {else if}
         else if opcode = pc_ldc then begin
            if op^.optype in [cgLong, cgULong] then begin
               op^.left^.lval := op^.left^.lval + op^.q;
               opv := op^.left;
               end {if}
            else if op^.optype in [cgUByte, cgByte, cgUWord, cgWord] then begin
               op^.left^.q := op^.left^.q + op^.q;
               opv := op^.left;
               end; {else if}
            end {else if}
         else if opcode in [pc_lao,pc_lda] then begin
            op^.left^.q := op^.left^.q + op^.q;
            opv := op^.left;
            end; {else if}
         end; {else}
      end; {case pc_inc}

   pc_ind: begin			{pc_ind}
      opcode := op^.left^.opcode;
      if opcode = pc_lda then begin
         op^.left^.opcode := pc_lod;
         op^.left^.optype := op^.optype;
         op^.left^.q := op^.left^.q + op^.q;
         opv := op^.left;
         end {if}
      else if opcode = pc_lao then begin
         op^.left^.opcode := pc_ldo;
         op^.left^.optype := op^.optype;
         op^.left^.q := op^.left^.q + op^.q;
         opv := op^.left;
         end {else if}
      else if opcode = pc_inc then begin
         if op^.left^.optype = cgULong then begin
            if ord4(op^.left^.q) + ord4(op^.q) < ord4(maxint - 1) then begin
               op^.q := op^.q + op^.left^.q;
               op^.left := op^.left^.left;
               PeepHoleOptimization(opv);
               end; {if}
            end; {if}
         end; {else if}
      end; {case pc_ind}

   pc_ior: begin			{pc_ior}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.q := ord((op^.left^.q <> 0) or (op^.right^.q <> 0));
            opv := op^.left;
            end {if}
         else begin
            if op^.right^.q <> 0 then begin
               if not SideEffects(op^.left) then begin
                  op^.right^.q := 1;
                  opv := op^.right;
                  end; {if}
               end {if}
            else begin
               op^.opcode := pc_neq;
               PeepHoleOptimization(opv);
               end; {else}
            end {if}
         end {if}
      else if op^.left^.opcode = pc_ldc then
         if op^.left^.q <> 0 then begin
            op^.left^.q := 1;
            opv := op^.left;
            end; {if}
      end; {case pc_ior}

   pc_ixa: begin			{pc_ixa}
      if op^.right^.opcode = pc_ldc then begin
         optype := op^.optype;
         if optype in [cgUByte, cgByte, cgUWord, cgWord] then begin
            lval := op^.right^.q;
            if optype = cgUByte then
               lval := lval & $000000FF
            else if optype = cgUWord then
               lval := lval & $0000FFFF;
            done := false;
            if op^.left^.opcode in [pc_lao, pc_lda] then begin
               lq := op^.left^.q + lval;
               if (lq >= 0) and (lq < maxint) then begin
                  done := true;
                  op^.left^.q := ord(lq);
                  opv := op^.left;
                  end; {if}
               end; {if}
            if not done then begin
               op^.right^.lval := lval;
               op^.right^.optype := cgLong;
               op^.opcode := pc_adl;
               PeepHoleOptimization(opv);
               end; {if}
            end; {if}                   
         end {if}
      else if op^.left^.opcode = pc_lao then begin
         if op^.right^.opcode = pc_inc then begin
            lq := ord4(op^.right^.q) + ord4(op^.left^.q);
            if lq < maxint then begin
               op^.left^.q := ord(lq);
               op^.right := op^.right^.left;
               end; {if}
            PeepHoleOptimization(opv);
            end; {if}
         end {else if}
      else if op^.left^.opcode = pc_ixa then begin
         if smallMemoryModel then
            if op^.left^.left^.opcode in [pc_lao,pc_lda] then
               if op^.left^.left^.q = 0 then begin
                  op2 := op^.left;
                  op^.left := op^.left^.left;
                  op2^.left := op^.right;
                  op2^.opcode := pc_adi;
                  op^.right := op2;
                  op^.optype := cgUWord;
                  end; {if}
         end; {else if}
      end; {case pc_ixa}

   pc_leq, pc_les, pc_geq, pc_grt: begin  {pc_leq, pc_les, pc_geq, pc_grt}
      if op^.left^.opcode = pc_ldc then begin
         ReverseChildren(op);
         case op^.opcode of
            pc_leq: op^.opcode := pc_geq;
            pc_les: op^.opcode := pc_grt;
            pc_geq: op^.opcode := pc_leq;
            pc_grt: op^.opcode := pc_les;
            end; {case}
         end; {if}
      if (op^.optype = cgWord) then
         if (TypeOf(op^.right) = cgUByte)
            or ((op^.right^.opcode = pc_ldc) and (op^.right^.q >= 0)
                and (op^.right^.optype in [cgByte,cgUByte,cgWord])) then
            if (TypeOf(op^.left) = cgUByte)
               or ((op^.left^.opcode = pc_ldc) and (op^.left^.q >= 0)
                   and (op^.left^.optype in [cgByte,cgUByte,cgWord])) then
               op^.optype := cgUWord;
      if op^.right^.opcode = pc_ldc then
         if ((op^.optype = cgUWord) and (op^.right^.q = 0))
            or ((op^.optype = cgULong) and (op^.right^.lval = 0))
            or ((op^.optype = cgUQuad) 
               and (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0)) then
            begin
            case op^.opcode of
               pc_leq: begin
                       op^.opcode := pc_equ;
                       PeepHoleOptimization(opv);
                       end;
               pc_grt: begin
                       op^.opcode := pc_neq;
                       PeepHoleOptimization(opv);
                       end;
               pc_les: if not SideEffects(op^.left) then begin
                          op^.right^.optype := cgWord;
                          op^.right^.q := 0;
                          opv := op^.right;
                          end; {if}
               pc_geq: if not SideEffects(op^.left) then begin
                          op^.right^.optype := cgWord;
                          op^.right^.q := 1;
                          opv := op^.right;
                          end; {if}
               end; {case}
            end {if}
         else if (op^.opcode = pc_leq) then
            if ((op^.optype = cgWord) and (op^.right^.q <> maxint))
               or ((op^.optype = cgUWord) and (op^.right^.q <> -1)) then begin
               op^.right^.q := op^.right^.q + 1;
               op^.opcode := pc_les;
               end; {if}
      end; {case pc_leq, pc_les, pc_geq, pc_grt}

   pc_lnd: begin			{pc_lnd}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.q := ord((op^.left^.lval <> 0) and (op^.right^.lval <> 0));
            op^.left^.optype := cgWord;
            opv := op^.left;
            end {if}
         else begin
            if op^.right^.lval = 0 then begin
               if not SideEffects(op^.left) then  begin
                  with op^.right^ do begin
                     lval := 0;
                     optype := cgWord;
                     q := 0;
                     end; {with}
                  opv := op^.right;
                  end; {if}
               end; {if}
            end; {if}
         end {if}
      else if op^.left^.opcode = pc_ldc then
         if op^.left^.lval = 0 then begin
            with op^.left^ do begin
               lval := 0;
               optype := cgWord;
               q := 0;
               end; {with}
            opv := op^.left;
            end; {if}
      end; {case pc_lnd}

   pc_lnm: begin			{pc_lnm}
      if op^.next^.opcode = pc_lnm then begin
         opv := op^.next;
         rescan := true;
         end; {if}
      end; {case pc_lnm}

   pc_lor: begin			{pc_lor}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.q := ord((op^.left^.lval <> 0) or (op^.right^.lval <> 0));
            optype := cgWord;
            opv := op^.left;
            end {if}
         else begin
            if op^.right^.lval <> 0 then begin
               if not SideEffects(op^.left) then begin
                  op^.right^.lval := 0;
                  op^.right^.q := 1;
                  op^.right^.optype := cgWord;
                  opv := op^.right;
                  end; {if}
               end {if}
            else begin
               op^.opcode := pc_neq;
               op^.optype := cgLong;
               PeepHoleOptimization(opv);
               end; {else}
            end; {if}
         end {if}
      else if op^.left^.opcode = pc_ldc then
         if op^.left^.lval <> 0 then begin
            op^.left^.lval := 0;
            op^.left^.q := 1;
            op^.left^.optype := cgWord;
            opv := op^.left;
            end; {if}
      end; {case pc_lor}

   pc_mdl: begin			{pc_mdl}
      if op^.right^.opcode = pc_ldc then 
         if op^.right^.lval = 1 then begin
            if not SideEffects(op^.left) then begin
               op^.right^.lval := 0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then 
            if (op^.left^.lval >= 0) and (op^.right^.lval > 0) then begin
               op^.left^.lval := op^.left^.lval mod op^.right^.lval;
               opv := op^.left;
               end; {if}
      end; {case pc_mdl}

   pc_mdq: begin			{pc_mdq}
      if op^.right^.opcode = pc_ldc then 
         if (op^.right^.qval.lo = 1) and (op^.right^.qval.hi = 0) then begin
            if not SideEffects(op^.left) then begin
               op^.right^.qval := longlong0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then
            if (op^.right^.qval.lo <> 0) or (op^.right^.qval.hi <> 0) then begin
               rem64(op^.left^.qval, op^.right^.qval);
               opv := op^.left;
               end; {if}
      end; {case pc_mdq}

   pc_mod: begin			{pc_mod}
      if op^.right^.opcode = pc_ldc then 
         if op^.right^.q = 1 then begin
            if not SideEffects(op^.left) then begin
               op^.right^.q := 0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then 
            if (op^.left^.q >= 0) and (op^.right^.q > 0) then begin
               op^.left^.q := op^.left^.q mod op^.right^.q;
               opv := op^.left;
               end; {if}
      end; {case pc_mod}

   pc_mpi, pc_umi: begin		{pc_mpi, pc_umi}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         if op^.opcode = pc_mpi then
            op^.left^.q := op^.left^.q*op^.right^.q
         else {if op^.opcode = pc_umi then} begin
            lval := umul(op^.left^.q & $0000FFFF, op^.right^.q & $0000FFFF);
            op^.left^.q := long(lval).lsw;
            end; {else}
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            q := op^.right^.q;
            if q = 1 then
               opv := op^.left
            else if q = 0 then begin
               if not SideEffects(op^.left) then
                  opv := op^.right;
               end {else if}
            else if (q = -1) and (op^.opcode = pc_mpi) then begin
               op^.opcode := pc_ngi;
               op^.right := nil;
               end {else if}
            else if OneBit(q) then begin
               op^.right^.q := Base(q);
               op^.opcode := pc_shl;
               PeepHoleOptimization(opv);
               end; {else if}
            end; {if}
         end; {else}
      end; {case pc_mpi, pc_umi}

   pc_mpl, pc_uml: begin		{pc_mpl, pc_uml}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         if op^.opcode = pc_mpl then
            op^.left^.lval := op^.left^.lval*op^.right^.lval
         else {if op^.opcode = pc_uml then}
            op^.left^.lval := umul(op^.left^.lval, op^.right^.lval);
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            lval := op^.right^.lval;
            if lval = 1 then
               opv := op^.left
            else if lval = 0 then begin
               if not SideEffects(op^.left) then
                  opv := op^.right;
               end {else if}
            else if (lval = -1) and (op^.opcode = pc_mpl) then begin
               op^.opcode := pc_ngl;
               op^.right := nil;
               end {else if}
            else if OneBit(lval) then begin
               op^.right^.lval := Base(lval);
               op^.opcode := pc_sll;
               end; {else if}
            end; {if}
         end; {else}
      end; {case pc_mpl, pc_uml}

   pc_mpq, pc_umq: begin		{pc_mpq, pc_umq}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         umul64(op^.left^.qval, op^.right^.qval);
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            if (op^.right^.qval.lo = 1) and (op^.right^.qval.hi = 0) then
               opv := op^.left
            else if (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0) then
               begin
               if not SideEffects(op^.left) then
                  opv := op^.right;
               end {else if}
            else if (op^.right^.qval.lo = -1) and (op^.right^.qval.hi = -1) then
               if op^.opcode = pc_mpq then begin
                  op^.opcode := pc_ngq;
                  op^.right := nil;
                  end; {if}
            end; {if}
         end; {else}
      end; {case pc_mpq, pc_umq}

   pc_mpr: begin			{pc_mpr}
      if (op^.right^.opcode = pc_ldc) and (op^.left^.opcode = pc_ldc) then begin
         op^.left^.rval := op^.left^.rval*op^.right^.rval;
         opv := op^.left;
         end {if}
      else begin
         if op^.left^.opcode = pc_ldc then
            ReverseChildren(op);
         if op^.right^.opcode = pc_ldc then begin
            rval := op^.right^.rval;
            if rval = 1.0 then
               opv := op^.left
            else if rval = 0.0 then
               if fastMath then
                  if not SideEffects(op^.left) then
                     opv := op^.right;
            end; {if}
         end; {else}
      end; {case pc_mpr}

   pc_neq: begin			{pc_neq}
      if op^.left^.opcode = pc_ldc then
         ReverseChildren(op);
      if op^.right^.opcode = pc_ldc then begin
	 if op^.left^.opcode = pc_ldc then begin
            BinOps(op^.left, op^.right);
            case op^.left^.optype of
               cgByte,cgUByte,cgWord,cgUWord: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.q <> op^.right^.q);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgLong,cgULong: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.lval <> op^.right^.lval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgQuad,cgUQuad: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord((op^.left^.qval.lo <> op^.right^.qval.lo) or
        	               (op^.left^.qval.hi <> op^.right^.qval.hi));
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgReal,cgDouble,cgComp,cgExtended: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.rval <> op^.right^.rval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               cgVoid,ccPointer: begin
        	  op^.opcode := pc_ldc;
        	  op^.q := ord(op^.left^.pval <> op^.right^.pval);
        	  op^.left := nil;
        	  op^.right := nil;
        	  end;
               end; {case}
            op^.optype := cgWord;
            end {if}
         else if op^.right^.optype in [cgByte, cgUByte, cgWord, cgUWord] then begin
            if op^.right^.q = 0 then
               if op^.left^.opcode in
         	  [pc_and,pc_ior,pc_neq,pc_equ,pc_geq,pc_leq,pc_les,pc_grt,pc_not]
                  then begin
                  opv := op^.left;
                  opv^.next := op^.next;
                  end; {if}
            end {else if}
         else if op^.right^.optype in [cgLong, cgULong] then begin
            if op^.right^.lval = 0 then
               if op^.left^.opcode in
         	  [pc_and,pc_ior,pc_neq,pc_equ,pc_geq,pc_leq,pc_les,pc_grt,pc_not]
                  then begin
                  opv := op^.left;
                  opv^.next := op^.next;
                  end; {if}
            end; {else if}
         end; {if}                      
      end; {case pc_neq}

   pc_ngi: begin			{pc_ngi}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.q := -op^.left^.q;
         opv := op^.left;
         end; {if}
      end; {case pc_ngi}

   pc_ngl: begin			{pc_ngl}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.lval := -op^.left^.lval;
         opv := op^.left;
         end; {if}
      end; {case pc_ngl}

   pc_ngq: begin			{pc_ngq}
      if op^.left^.opcode = pc_ldc then begin
         with op^.left^.qval do begin
            lo := ~lo;
            hi := ~hi;
            lo := lo + 1;
            if lo = 0 then
               hi := hi + 1;
            end; {with}
         opv := op^.left;
         end; {if}
      end; {case pc_ngq}

   pc_ngr: begin			{pc_ngr}
      if op^.left^.opcode = pc_ldc then begin
         op^.left^.rval := -op^.left^.rval;
         opv := op^.left;
         end; {if}
      end; {case pc_ngr}

   pc_not: begin			{pc_not}
      opcode := op^.left^.opcode;
      if opcode = pc_ldc then begin
         if op^.left^.optype in [cgByte,cgUByte,cgWord,cgUWord] then begin
            op^.left^.q := ord(op^.left^.q = 0);
            opv := op^.left;
            end {if}
         else if op^.left^.optype in [cgLong,cgULong] then begin
            q := ord(op^.left^.lval = 0);
            op^.left^.q := q;
            op^.left^.optype := cgWord;
            opv := op^.left;
            end {else if}
         else if op^.left^.optype in [cgQuad,cgUQuad] then begin
            q := ord((op^.left^.qval.lo = 0) and (op^.left^.qval.hi = 0));
            op^.left^.q := q;
            op^.left^.optype := cgWord;
            opv := op^.left;
            end; {else if}
         end {if}
      else if opcode = pc_equ then begin
         op^.left^.opcode := pc_neq;
         opv := op^.left;
         end {else if}
      else if opcode = pc_neq then begin
         op^.left^.opcode := pc_equ;
         opv := op^.left;
         end {else if}
      else if opcode = pc_geq then begin
         op^.left^.opcode := pc_les;
         opv := op^.left;
         end {else if}
      else if opcode = pc_grt then begin
         op^.left^.opcode := pc_leq;
         opv := op^.left;
         end {else if}
      else if opcode = pc_les then begin
         op^.left^.opcode := pc_geq;
         opv := op^.left;
         end {else if}
      else if opcode = pc_leq then begin
         op^.left^.opcode := pc_grt;
         opv := op^.left;
         end; {else if}
      end; {case pc_not}

   pc_pop: begin			{pc_pop}
      if op^.left^.opcode = pc_cnv then begin
         fromtype.i := (op^.left^.q & $00F0) >> 4;
         op^.optype := fromtype.optype;
         op^.left := op^.left^.left;
         end; {if}
      opcode := op^.left^.opcode;
      if opcode = pc_cop then begin
         op^.left^.opcode := pc_str;
         opv := op^.left;
         opv^.next := op^.next;
         PeepHoleOptimization(opv);
         end {if}            
      else if opcode = pc_cpi then begin
         op^.left^.opcode := pc_sto;
         opv := op^.left;
         opv^.next := op^.next;
         PeepHoleOptimization(opv);
         end {else if}
      else if opcode = pc_cbf then begin
         op^.left^.opcode := pc_sbf;
         opv := op^.left;
         opv^.next := op^.next;
         end {else if}
      else if opcode = pc_cpo then begin
         op^.left^.opcode := pc_sro;
         opv := op^.left;
         opv^.next := op^.next;
         PeepHoleOptimization(opv);
         end {else if}
      else if opcode in [pc_inc,pc_dec] then
         op^.left := op^.left^.left;
      end; {case pc_pop}

   pc_ret: begin			{pc_ret}
      RemoveDeadCode(op);
      end; {case pc_ret}

   pc_sbi: begin			{pc_sbi}
      if op^.left^.opcode = pc_ldc then begin
         if op^.right^.opcode = pc_ldc then begin
            op^.left^.q := op^.left^.q - op^.right^.q;
            opv := op^.left;
            end {if}
         else if op^.left^.q = 0 then begin
            op^.opcode := pc_ngi;
            op^.left := op^.right;
            op^.right := nil;
            end; {else if}
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         q := op^.right^.q;
         if q = 0 then
            opv := op^.left
         else if (q > 0) then begin
            op^.opcode := pc_dec;
            op^.q := q;
            op^.right := nil;
            PeepHoleOptimization(opv);
            end {else if}
         else {if q < 0) then} begin
            op^.opcode := pc_inc;
            op^.q := -q;
            op^.right := nil;
            PeepHoleOptimization(opv);
            end; {else if}
         end {if}
      else if op^.left^.opcode in [pc_inc,pc_dec] then
         if op^.right^.opcode in [pc_inc,pc_dec] then begin
            op2 := op^.left;
            if op^.left^.opcode = pc_inc then
               q := op^.left^.q
            else
               q := -op^.left^.q;
            if op^.right^.opcode = pc_inc then
               q := q - op^.right^.q
            else
               q := q + op^.right^.q;
            if q >= 0 then begin
               op2^.opcode := pc_inc;
               op2^.q := q;
               end {if}
            else begin
               op2^.opcode := pc_dec;
               op2^.q := -q;
               end; {else}
            op^.left := op^.left^.left;
            op^.right := op^.right^.left;
            op2^.left := op;
            opv := op2;
            PeepHoleOptimization(opv);
            end; {if}
      end; {case pc_sbi}

   pc_sbl: begin			{pc_sbl}
      if op^.left^.opcode = pc_ldc then begin
         if op^.right^.opcode = pc_ldc then begin
            op^.left^.lval := op^.left^.lval - op^.right^.lval;
            opv := op^.left;
            end {if}
         else if op^.left^.lval = 0 then begin
            op^.opcode := pc_ngl;
            op^.left := op^.right;
            op^.right := nil;
            end; {else if}
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         lval := op^.right^.lval;
         if lval = 0 then
            opv := op^.left
         else if (lval > 0) and (lval <= maxint) then begin
            op^.opcode := pc_dec;
            op^.q := ord(lval);
            op^.right := nil;
            op^.optype := cgLong;
            PeepHoleOptimization(opv);
            end {else if}
         else if (lval > -maxint) and (lval < 0) then begin
            op^.opcode := pc_inc;
            op^.q := -ord(lval);
            op^.right := nil;
            op^.optype := cgLong;
            PeepHoleOptimization(opv);
            end; {else if}       
         end; {if}
      end; {case pc_sbl}

   pc_sbr: begin			{pc_sbr}
      if op^.left^.opcode = pc_ldc then begin
         if op^.right^.opcode = pc_ldc then begin
            op^.left^.rval := op^.left^.rval - op^.right^.rval;
            opv := op^.left;
            end {if}
         else if op^.left^.rval = 0.0 then
            if fastMath then begin
               op^.opcode := pc_ngr;
               op^.left := op^.right;
               op^.right := nil;
               end; {if}
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if fastMath then
            if op^.right^.rval = 0.0 then
               opv := op^.left;
         end; {if}
      end; {case pc_sbr}

   pc_sbq: begin			{pc_sbq}
      if op^.left^.opcode = pc_ldc then begin
         if op^.right^.opcode = pc_ldc then begin
            sub64(op^.left^.qval, op^.right^.qval);
            opv := op^.left;
            end {if}
         else if (op^.left^.qval.lo = 0) and (op^.left^.qval.hi = 0) then begin
            op^.opcode := pc_ngq;
            op^.left := op^.right;
            op^.right := nil;
            end; {else if}
         end {if}
      else if op^.right^.opcode = pc_ldc then begin
         if (op^.right^.qval.lo = 0) and (op^.right^.qval.hi = 0) then
            opv := op^.left;
         end; {if}
      end; {case pc_sbq}

   pc_shl: begin			{pc_shl}
      if op^.right^.opcode = pc_ldc then begin
         opcode := op^.left^.opcode;
         if opcode = pc_ldc then begin
            op^.left^.q := op^.left^.q << op^.right^.q;
            opv := op^.left;
            end {if}
         else if opcode = pc_shl then begin
            if op^.left^.right^.opcode = pc_ldc then begin
               op^.right^.q := op^.right^.q + op^.left^.right^.q;
               op^.left := op^.left^.left;
               end; {if}
            end {if}
         else if opcode = pc_inc then begin
            op2 := op^.left;
            op^.left := op2^.left;
            op2^.q := op2^.q << op^.right^.q;
            op2^.left := op;
            opv := op2;
            PeepHoleOptimization(op2^.left);
            end {else if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_shl}

   pc_shr: begin			{pc_shr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.q := op^.left^.q >> op^.right^.q;
            opv := op^.left;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_shr}

   pc_sll: begin			{pc_sll}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.lval := op^.left^.lval << op^.right^.lval;
            opv := op^.left;
            end {if}
         else if op^.right^.lval = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_sll}

   pc_slr: begin			{pc_slr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.lval := op^.left^.lval >> op^.right^.lval;
            opv := op^.left;
            end {if}
         else if op^.right^.lval = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_slr}

   pc_slq: begin			{pc_slq}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            shl64(op^.left^.qval, op^.right^.q);
            opv := op^.left;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_slq}

   pc_sro, pc_str: begin		{pc_sro, pc_str}
      if op^.optype in [cgReal,cgDouble,cgExtended,cgComp] then
         RealStoreOptimizations(op, op^.left);
      end; {case pc_sro, pc_str}

   pc_sto: begin			{pc_sto}
      op2 := op^.right;
      if op^.left^.opcode = pc_lao then begin
         op^.q := op^.left^.q;
         op^.lab := op^.left^.lab;
         op^.opcode := pc_sro;
         op^.left := op2;
         op^.right := nil;
         end {if}
      else if op^.left^.opcode = pc_lda then begin
         op^.q := op^.left^.q;
         op^.r := op^.left^.r;
         op^.opcode := pc_str;
         op^.left := op2;
         op^.right := nil;
         end; {if}
      if op^.optype in [cgReal,cgDouble,cgExtended,cgComp] then
         RealStoreOptimizations(op, op2);
      end; {case pc_sto}

   pc_sqr: begin			{pc_sqr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            ashr64(op^.left^.qval, op^.right^.q);
            opv := op^.left;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_sqr}

   pc_tjp: begin			{pc_tjp}
      opcode := op^.left^.opcode;
      if opcode = pc_ldc then begin
         if op^.left^.optype in [cgByte, cgUByte, cgWord, cgUWord] then
            if op^.left^.q = 0 then begin
               opv := op^.next;
               rescan := true;
               end {if}
            else begin
               op^.opcode := pc_ujp;
               op^.left := nil;
               PeepHoleOptimization(opv);
               end; {else}
         end {if}
      else if opcode = pc_ior then begin
         op2 := op^.left;
         op2^.next := op^.next;
         op^.next := op2;
         op^.left := op2^.left;
         op2^.left := op2^.right;
         op2^.right := nil;
         op2^.opcode := pc_tjp;
         op2^.q := op^.q;
         PeepHoleOptimization(opv);
         end {else if}
      else if opcode = pc_and then begin
         op2 := op^.left;
         op2^.next := op^.next;
         op^.next := op2;
         op^.left := op2^.left;
         op2^.left := op2^.right;
         op2^.right := nil;
         op2^.opcode := pc_tjp;
         op2^.q := op^.q;
         op^.opcode := pc_fjp;
         op3 := pointer(Calloc(sizeof(intermediate_code)));
         op3^.opcode := dc_lab;
         op3^.optype := cgWord;
         op3^.q := GenLabel;
         op3^.next := op2^.next;
         op2^.next := op3;
         op^.q := op3^.q;
         PeepHoleOptimization(opv);
         end {else if}
      else
         JumpOptimizations(op, pc_fjp);
      end; {case pc_tjp}

   pc_tri: begin			{pc_tri}
      opcode := op^.left^.opcode;
      if opcode = pc_not then begin
         ReverseChildren(op^.right);
         op^.left := op^.left^.left;
         PeepHoleOptimization(opv);
         end {if}
      else if opcode in [pc_equ, pc_neq] then begin
	 with op^.left^.right^ do
            if opcode = pc_ldc then
               if optype in [cgByte,cgUByte,cgWord,cgUWord] then
        	  if q = 0 then begin
                     if op^.left^.opcode = pc_equ then
			ReverseChildren(op^.right);
                     op^.left := op^.left^.left;
                     end; {if}
         end {else if}
      else if opcode = pc_ldc then
         if op^.left^.optype in [cgWord,cgUWord] then
            if op^.next = nil then
               if op^.left^.q <> 0 then begin
                  if op^.optype = TypeOf(op^.right^.left) then
                     opv := op^.right^.left;
                  end {if}
               else
                  if op^.optype = TypeOf(op^.right^.right) then
                     opv := op^.right^.right;
      end; {case pc_tri}

   pc_udi: begin			{pc_udi}
      if op^.right^.opcode = pc_ldc then begin
         q := op^.right^.q;
         if op^.left^.opcode = pc_ldc then begin
            if q <> 0 then begin
               op^.left^.q := ord(udiv(op^.left^.q & $0000FFFF, q & $0000FFFF));
               opv := op^.left;
               end; {if}
            end {if}
         else if q = 1 then
            opv := op^.left
         else if OneBit(q) then begin
            op^.right^.q := Base(q);
            op^.opcode := pc_usr;
            end; {else if}
         end; {if}
      end; {case pc_udi}

   pc_udl: begin			{pc_udl}
      if op^.right^.opcode = pc_ldc then begin
         lq := op^.right^.lval;
         if op^.left^.opcode = pc_ldc then begin
            if lq <> 0 then begin
               op^.left^.lval := udiv(op^.left^.lval, lq);
               opv := op^.left;
               end; {if}
            end {if}
         else if lq = 1 then
            opv := op^.left
         else if OneBit(lq) then begin
            op^.right^.lval := Base(lq);
            op^.opcode := pc_vsr;
            end; {else if}
         end; {if}
      end; {case pc_udl}

   pc_udq: begin			{pc_udq}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            if (op^.right^.qval.lo <> 0) or (op^.right^.qval.hi <> 0) then begin
               udiv64(op^.left^.qval, op^.right^.qval);
               opv := op^.left;
               end; {if}
            end {if}
         else if (op^.right^.qval.lo = 1) and (op^.right^.qval.hi = 0) then
            opv := op^.left;
         end; {if}
      end; {case pc_udq}

   pc_uim: begin			{pc_uim}
      if op^.right^.opcode = pc_ldc then 
         if op^.right^.q = 1 then begin
            if not SideEffects(op^.left) then begin
               op^.right^.q := 0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then 
            if op^.right^.q <> 0 then begin
               op^.left^.q :=
                  ord(umod(op^.left^.q & $0000FFFF, op^.right^.q & $0000FFFF));
               opv := op^.left;
               end; {if}
      end; {case pc_uim}

   pc_ujp: begin			{pc_ujp}
      RemoveDeadCode(op);
      if op^.next^.opcode = dc_lab then begin
         if op^.q = op^.next^.q then begin
            opv := op^.next;
            rescan := true;
            end {if}
         else if op^.next^.next^.opcode = dc_lab then
            if op^.next^.next^.q = op^.q then begin
               opv := op^.next;
               rescan := true;
               end; {if}
         end; {if}
      end; {case pc_ujp}

   pc_ulm: begin			{pc_ulm}
      if op^.right^.opcode = pc_ldc then 
         if op^.right^.lval = 1 then begin
            if not SideEffects(op^.left) then begin
               op^.right^.lval := 0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then 
            if op^.right^.lval <> 0 then begin
               op^.left^.lval := umod(op^.left^.lval, op^.right^.lval);
               opv := op^.left;
               end; {if}
      end; {case pc_ulm}

   pc_uqm: begin			{pc_uqm}
      if op^.right^.opcode = pc_ldc then 
         if (op^.right^.qval.lo = 1) and (op^.right^.qval.hi = 0) then begin
            if not SideEffects(op^.left) then begin
               op^.right^.qval := longlong0;
               opv := op^.right;
               end; {if}
            end {if}
         else if op^.left^.opcode = pc_ldc then
            if (op^.right^.qval.lo <> 0) or (op^.right^.qval.hi <> 0) then begin
               umod64(op^.left^.qval, op^.right^.qval);
               opv := op^.left;
               end; {if}
      end; {case pc_uqm}

   pc_usr: begin			{pc_usr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            lval := lshr(op^.left^.q & $0000FFFF, op^.right^.q);
            op^.left^.q := long(lval).lsw;
            opv := op^.left;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_usr}

   pc_vsr: begin			{pc_vsr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            op^.left^.lval := lshr(op^.left^.lval, op^.right^.lval);
            opv := op^.left;
            end {if}
         else if op^.right^.lval = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_vsr}

   pc_wsr: begin			{pc_wsr}
      if op^.right^.opcode = pc_ldc then begin
         if op^.left^.opcode = pc_ldc then begin
            lshr64(op^.left^.qval, op^.right^.q);
            opv := op^.left;
            end {if}
         else if op^.right^.q = 0 then
            opv := op^.left;
         end; {if}
      end; {case pc_wsr}

   otherwise: ;
   end; {case}
end; {PeepHoleOptimization}

{- Common Subexpression Elimination ----------------------------}

function MatchLoc (op1, op2: icptr): boolean;

{ See if two loads, stores or copies refer to the same		}
{ location							}
{								}
{ parameters:							}
{    op1, op2 - operations to check				}
{								}
{ Returns: True if they do, false if they don't.		}

begin {MatchLoc}
MatchLoc := false;
if (op1^.opcode in [pc_str,pc_cop,pc_lod,pc_lli,pc_lil,pc_lld,pc_ldl,pc_lda])
   and (op2^.opcode in [pc_str,pc_cop,pc_lod,pc_lli,pc_lil,pc_lld,pc_ldl,pc_lda]) then begin
   if op1^.r = op2^.r then
      MatchLoc := true;
   end {if}
else if (op1^.opcode in [pc_sro,pc_cpo,pc_ldo,pc_gli,pc_gil,pc_gld,pc_gdl,pc_lao])
   and (op2^.opcode in [pc_sro,pc_cpo,pc_ldo,pc_gli,pc_gil,pc_gld,pc_gdl,pc_lao]) then
   if op1^.lab^ = op2^.lab^ then
      MatchLoc := true;
end; {MatchLoc}                       


function Member (op: icptr; list: iclist): boolean;

{ See if the operand of a load is referenced in a list		}
{								}
{ parameters:							}
{    op - load to check						}
{    list - list to check					}
{								}
{ Returns: True if op is in list, else false.			}
{								}
{ Notes: As a side effect, this subroutine sets memberOp to	}
{    point to any matching member; memberOp is undefined if	}
{    there is no matching member.				}

begin {Member}
Member := false;
while list <> nil do begin
   if MatchLoc(op, list^.op) then begin
      Member := true;
      memberOp := list^.op;
      list := nil;
      end {if}
   else
      list := list^.next;
   end; {while}
end; {Member}


function TypeOf {(op: icptr): baseTypeEnum};

{ find the type for the expression tree				}
{								}
{ parameters:							}
{    op - tree for which to find the type			}
{								}
{ Returns: base type						}

begin {TypeOf}
case op^.opcode of
   pc_gil, pc_gli, pc_gdl, pc_gld, pc_iil, pc_ili, pc_idl, pc_ild,
   pc_ldc, pc_ldo, pc_lil, pc_lli, pc_ldl, pc_lld, pc_lod, pc_dec,
   pc_inc, pc_ind, pc_lbf, pc_lbu, pc_cop, pc_cbf, pc_cpi, pc_cpo,
   pc_tri, pc_cup, pc_cui:
      TypeOf := op^.optype;

   pc_lad, pc_lao, pc_lca, pc_lda, pc_psh, pc_ixa:
      TypeOf := cgULong;

   pc_nop, pc_bnt, pc_ngi, pc_not, pc_adi, pc_and, pc_lnd, pc_bnd,
   pc_bor, pc_bxr, pc_dvi, pc_equ, pc_geq, pc_grt, pc_leq, pc_les,
   pc_neq, pc_ior, pc_lor, pc_mod, pc_mpi, pc_sbi, pc_shl, pc_shr:
      TypeOf := cgWord;

   pc_udi, pc_uim, pc_umi, pc_usr, pc_rbo:
      TypeOf := cgUWord;
                         
   pc_bnl, pc_ngl, pc_adl, pc_bal, pc_blr, pc_blx, pc_dvl, pc_mdl,
   pc_mpl, pc_sbl, pc_sll, pc_slr:
      TypeOf := cgLong;

   pc_udl, pc_ulm, pc_uml, pc_vsr:
      TypeOf := cgULong;

   pc_bnq, pc_ngq, pc_bqr, pc_bqx, pc_baq, pc_adq, pc_sbq, pc_mpq,
   pc_dvq, pc_mdq, pc_slq, pc_sqr:
      TypeOf := cgQuad;

   pc_umq, pc_udq, pc_uqm, pc_wsr:
      TypeOf := cgUQuad;

   pc_ngr, pc_adr, pc_dvr, pc_mpr, pc_sbr:
      TypeOf := cgExtended;

   pc_cnn, pc_cnv:                                        
      TypeOf := baseTypeEnum(op^.q & $000F);

   pc_stk, pc_ckp:
      TypeOf := TypeOf(op^.left);

   pc_bno:
      TypeOf := TypeOf(op^.right);

   pc_tl1:				{pc_tl1 doesn't have type info.}
      TypeOf := cgVoid;			{Just return cgVoid for now.}

   otherwise: Error(cge1);       
   end; {case}
end; {TypeOf}


procedure CommonSubexpressionElimination;

{ Remove common subexpressions					}

type
   localPtr = ^localRecord;		{list of local temp variables}
   localRecord = record
      next: localPtr;			{next label in list}
      inUse: boolean;			{is this temp already in use?}
      size: integer;			{size of the temp area}
      lab: integer;			{label number}
      end;

var
   bb: blockPtr;			{used to trace basic block lists}
   done: boolean;			{for loop termination tests}
   op: icptr;				{used to trace operation lists, trees}
   lop: icptr;				{predecessor of op}
   temps: localPtr;			{list of temp variables}


   procedure DisposeTemps;

   { dispose of the list of temp variables			}

   var
      tp: localPtr;			{temp pointer}

   begin {DisposeTemps}
   while temps <> nil do begin
      tp := temps;
      temps := tp^.next;
      dispose(tp);
      end; {while}
   end; {DisposeTemps}


   function GetTemp (bb: blockPtr; size: integer): integer;

   { Allocate a temp storage location				}
   {								}
   { parameters:						}
   {    bb - block in which the temp is allocated		}
   {    size - size of the temp					}
   {								}
   { Returns: local label number for the temp			}

   var
      lab: integer;			{label number}
      loc: icptr;			{for dc_loc instruction}
      tp: localPtr;			{used to trace lists, allocate new items}

   begin {GetTemp}
   lab := 0;				{no label found, yet}
   tp := temps;				{try for a temp of the exact size}
   while tp <> nil do begin
      if not tp^.inUse then
         if tp^.size = size then begin
            lab := tp^.lab;
            tp^.inUse := true;
            tp := nil;
            end; {if}
      if tp <> nil then
         tp := tp^.next;
      end; {while}
   if lab = 0 then begin		{try for a larger temp}
      tp := temps;
      while tp <> nil do begin
	 if not tp^.inUse then
            if tp^.size > size then begin
               lab := tp^.lab;
               tp^.inUse := true;
               tp := nil;        
               end; {if}
	 if tp <> nil then
            tp := tp^.next;
	 end; {while}
      end; {if}
   if lab = 0 then begin		{allocate a new temp}
      loc := pointer(Calloc(sizeof(intermediate_code)));
      loc^.opcode := dc_loc;
      loc^.optype := cgWord;
      maxLoc := maxLoc + 1;
      loc^.r := maxLoc;
      lab := maxLoc;
      loc^.q := size;
      if bb^.code = nil then begin
	 loc^.next := nil;
	 bb^.code := loc;
         end {if}
      else begin
	 loc^.next := bb^.code^.next;
	 bb^.code^.next := loc;
         end; {else}
      new(tp);
      tp^.next := temps;
      temps := tp;
      tp^.inUse := true;
      tp^.size := loc^.q;
      tp^.lab := lab;
      end; {if}
   GetTemp := lab;			{return the temp label number}
   end; {GetTemp}


   procedure ResetTemps;

   { Mark all temps as available				}

   var
      tp: localPtr;			{temp pointer}

   begin {ResetTemps}
   tp := temps;
   while tp <> nil do begin
      tp^.inUse := false;
      tp := tp^.next;
      end; {while}
   end; {ResetTemps}


   procedure CheckForBlocks (op: icptr);

   { Scan a tree for blocked instructions			}
   {								}
   { parameters:						}
   {    op - tree to check					}
   {								}
   { Notes: Some code takes less time to execute than saving	}
   {    and storing the intermediate value.  This subroutine	}
   {    identifies such patterns.				}


      function Block (op: icptr): boolean;

      { See if the pattern should be blocked			}
      {								}
      { parameters:						}
      {    op - pattern to check				}
      {								}
      { Returns: True if the pattern should be blocked, else	}
      {    false.						}

      var
         opcode: pcodes;		{temp opcode}

      begin {Block}
      Block := false;
      opcode := op^.opcode;
      if opcode = pc_ixa then begin
         if op^.left^.opcode in [pc_lao,pc_lca,pc_lda] then
            Block := true;
         end {else if}
      else if opcode = pc_shl then begin
         if op^.right^.opcode = pc_ldc then
            if op^.right^.q = 1 then
               if op^.parents <= 3 then
                  Block := true;
         end {else if}
      else if opcode = pc_stk then
         Block := true
      else if opcode = pc_psh then
         Block := true
      else if opcode = pc_cnv then
         if op^.q & $000F = ord(cgVoid) then
            Block := true;
      end; {Block}


      function Max (a, b: integer): integer;

      { Return the larger of two integers			}
      {								}
      { parameters:						}
      {    a, b - integers to check				}
      {								}
      { Returns: a if a > b, else b				}

      begin {Max}
      if a > b then
         Max := a
      else
         Max := b;
      end; {Max}


   begin {CheckForBlocks}
   if Block(op) then begin
      if op^.left <> nil then		{handle a blocked instruction}
         op^.left^.parents := op^.left^.parents + Max(op^.parents - 1, 0);
      if op^.right <> nil then
         op^.right^.parents := op^.right^.parents + Max(op^.parents - 1, 0);
      op^.parents := 1;
      end; {if}
   if op^.left <> nil then		{check the children}
      CheckForBlocks(op^.left);
   if op^.right <> nil then
      CheckForBlocks(op^.right);
   end; {CheckForBlocks}


   procedure CheckTree (var op: icptr; bb: blockPtr);

   { check the trees used by op for common subexpressions	}
   {								}
   { parameters:						}
   {    op - operation to check					}
   {    bb - start of the current BASIC block			}

   var
      op2: icptr;			{result from Match calls}
      op3: icptr;			{used to trace the codes in a block}


      function Match (var op: icptr; tree: icptr): icptr;

      { Check for matches to op in tree				}
      {								}
      { parameters:						}
      {    op - operation to check				}
      {    tree - tree to examine for matches			}
      {								}
      { Returns: pointer to matching node or nil if none found	}

      var
         op2: icptr;			{result from recursive Match calls}
         kill, start, stop: boolean;	{used by Scan}
         skip: boolean;			{used to see if children should be scanned}


         procedure Combine (var op1, op2: icptr);

         { Op2 is a save or copy of the same value as op1; use a copy	}
         { for op2.							}
         {								}
         { parameters:							}
         {    op1 - first copy or save					}
         {    op2 - copy or save to optimize				}

         var
            op3: icptr;				{work pointer}

         begin {Combine}
         done := false;				{force another labeling pass}
	 op3 := op2;				{remove op2 from the list}
         if op3^.opcode in [pc_str,pc_sro] then begin
            if op3^.opcode = pc_str then
               op3^.opcode := pc_cop
            else
               op3^.opcode := pc_cpo;
            op2 := op3^.next;
            op3^.next := nil;
            end {if}
         else
            op2 := op3^.left;
         if op2 = nil then begin
            op2 := pointer(Calloc(sizeof(intermediate_code)));
            op2^.opcode := pc_nop;
            op2^.optype := cgWord;
            end; {if}
         op1^.left := op3;			{place in the new location}
         end; {Combine}


         function SameTree (list, op1, op2: icptr): boolean;

         { Are op1 and op2 in the same expression tree?			}
         {								}
         { parameters:							}
         {    list - list of expression trees				}
         {    op1, op2 - operations to check				}


            function InTree (tree, op: icptr): boolean;

            { See if op is in the tree					}
            {								}
            { parameters:						}
            {    tree - expression tree to check			}
            {    op - operatio to look for				}

            begin {InTree}
            if tree = nil then
               InTree := false
            else if tree = op then
               InTree := true
            else
               InTree := InTree(tree^.left, op) or InTree(tree^.right, op);
            end; {InTree}


         begin {SameTree}
         SameTree := false;
         while list <> nil do
            if InTree(list, op1) then begin
               SameTree := InTree(list, op2);
               list := nil;
               end {if}
            else
               list := list^.next;
         end; {SameTree}


         procedure Scan (list, op1, op2: icptr);

         { Check to see if any operation between op1 and op2 kills the	}
         { optimization							}
         {								}
         { parameters:							}
         {    list - instruction stream					}
         {    op1 - starting operation					}
         {    op2 - ending operation					}
         {								}
         { globals:							}
         {    kill - set to true if the optimization must be blocked,	}
         {       or false if it can be performed			}
         {    start - has op1 been found?  (initialize to false)	}
         {    stop - has kill been set?	 (initialize to false)		}

         label 1;

         begin {Scan}
1:       if not start then			{see if it is time to start}
            if list = op1 then
               start := true;
         if list^.left <> nil then		{scan the children}
            Scan(list^.left, op1, op2);
         if not stop then
            if list^.right <> nil then
               Scan(list^.right, op1, op2);
         if start then				{check for a kill or termination}
            if not stop then
               if list = op2 then begin
        	  kill := false;
        	  stop := true;
        	  end {if}
        	                                {kill indirect accesses on stores}
        	                                {to indirectly-accessible locations}
               else if op1^.opcode in [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,
        	  pc_cup,pc_cui,pc_tl1,pc_ind,pc_sbf,pc_cbf] then begin
        	  if list^.opcode in [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,
        	  pc_cup,pc_cui,pc_tl1,pc_sbf,pc_cbf] then begin
        	     kill := true;
        	     stop := true;
        	     end {if}
        	  else if list^.opcode in [pc_str,pc_sro,pc_cop,pc_cpo,pc_lli,
        	  pc_lil,pc_lld,pc_ldl,pc_gli,pc_gil,pc_gld,pc_gdl] then
        	     if Member(list, c_ind) then begin
        	        kill := true;
        	        stop := true;
        	        end {if}
        	  end {else if}
               else if list^.opcode in [pc_str,pc_sro,pc_cop,pc_cpo,pc_lli,pc_lil,
        	  pc_lld,pc_ldl,pc_gli,pc_gil,pc_gld,pc_gdl] then begin
        	  if MatchLoc(list, op2) then begin
                     kill := true;
                     stop := true;
                     end {if}
        	  end {else if}
               else if list^.opcode in [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,
        	  pc_cup,pc_cui,pc_tl1,pc_sbf,pc_cbf] then
        	  if Member(op1, c_ind) or (op1^.opcode in [pc_lbf,pc_lbu]) then
                     begin
                     kill := true;
                     stop := true;
                     end; {if}
         if not stop then			{scan forward in the stream}
            if list^.next <> nil then begin
               list := list^.next;
               goto 1;
               end; {if}
         end; {Scan}


      begin {Match}
      op2 := nil;			{check for an exact match}
      skip := false;
      if not (op^.opcode in [pc_str,pc_sro]) and CodesMatch(op, tree, true)
         then begin
         if op = tree then
            op2 := tree
         else begin
            start := false;
            stop := false;
            Scan(bb^.code, tree, op);
            if not kill then
               op2 := tree;
            end; {else}
         end {if}
					{check for stores of a common value}
      else if op^.opcode in [pc_str,pc_sro,pc_cop,pc_cpo] then
         if tree^.opcode in [pc_str,pc_sro,pc_cop,pc_cpo] then
            if op^.left = tree^.left then begin
               start := false;
               stop := false;
               Scan(bb^.code, tree, op);
               if not kill then
                  if not SameTree(bb^.code, op, tree) then
                     if (op^.left^.opcode <> pc_ldc)
                        or ((op^.left^.optype in [cgByte,cgUByte,cgWord,cgUWord])
                           and (op^.left^.q <> 0))
                        or ((op^.left^.optype in [cgLong,cgULong])
                           and (op^.left^.lval <> 0))
                        or (not (op^.left^.optype in [cgByte,cgUByte,cgWord,cgUWord,cgLong,cgULong]))
                        then begin
                        Combine(tree, op);
                        skip := true;
                        end; {if}
               end; {if}
      if not skip then begin		{check for matches in the children}
         if op2 = nil then
	    if tree^.left <> nil then
               op2 := Match(op, tree^.left);
         if op2 = nil then
            if tree^.right <> nil then
               op2 := Match(op, tree^.right);
         end; {if}
      Match := op2;
      end; {Match}


   begin {CheckTree}
   op^.parents := 0;			{zero the parent counter}
   if op^.left <> nil then		{check the children}
      CheckTree(op^.left, bb);
   if op^.right <> nil then
      CheckTree(op^.right, bb);
   if op^.next = nil then		{look for a match to the current code}
      if not (op^.opcode in [pc_cup,pc_cui,pc_tl1,pc_bno,pc_pop,pc_sto,pc_sbf])
         then begin
	 op2 := nil;
	 op3 := bb^.code;
	 while (op2 = nil) and (op3 <> nil) do begin
	    op2 := Match(op, op3);
	    if op2 <> nil then
               if op2^.next = nil then begin
        	  op := op2;
        	  bb := nil;
        	  op3 := nil;
        	  end ;{if}
	    if op3 <> nil then
               op3 := op3^.next;
	    end; {while}
	 end; {if}
   end; {CheckTree}


   procedure CountParents (op: icptr);

   { increment the parent counter for all children of this node	}
   {								}
   { parameters:						}
   {    op - node for which to check the children		}

   begin {CountParents}
   if op^.parents = 0 then begin
      if op^.left <> nil then begin
	 CountParents(op^.left);
	 op^.left^.parents := op^.left^.parents + 1;
	 end; {if}
      if op^.right <> nil then begin
	 CountParents(op^.right);
	 op^.right^.parents := op^.right^.parents + 1;
	 end; {if}
      end; {if}
   end; {CountParents}


   procedure CreateTemps (var op: icptr; bb: blockPtr; var lop: icptr);

   { create temps for nodes with multiple parents		}
   {								}
   { parameters:						}
   {    op - node for which to create temps			}
   {    bb - current basic block				}
   {    lop - predecessor to op					}

   var
      children: boolean;		{does this node have children?}
      llab: integer;			{local label number; for temp}
      op2, str: icptr;			{new opcodes}
      optype: baseTypeEnum;		{type of the temp variable}

   begin {CreateTemps}
   children := false;			{create temps for the children}
   if op^.left <> nil then begin
      children := true;
      CreateTemps(op^.left, bb, lop);
      end; {if}
   if op^.right <> nil then begin
      children := true;
      CreateTemps(op^.right, bb, lop);
      end; {if}
   if children then
      if op^.parents > 1 then begin
         optype := TypeOf(op);		{create a temp label}
         llab := GetTemp(bb, TypeSize(optype));
					{make a copy of the duplicated tree}
         op2 := pointer(Calloc(sizeof(intermediate_code)));
         op2^ := op^;
         op^.opcode := pc_lod;		{substitute a load of the temp}
         op^.optype := optype;
         op^.parents := 1;
         op^.r := llab;
         op^.q := 0;
         op^.left := nil;
         op^.right := nil;
					{store the temp result}
         str := pointer(Calloc(sizeof(intermediate_code)));
         str^.opcode := pc_str;
         str^.optype := optype;
         str^.r := llab;
         {str^.q := 0;}
         str^.left := op2;
         if lop = nil then begin	{insert the store in the basic block}
            str^.next := bb^.code;
            bb^.code := str;
            end {if}
         else begin
            str^.next := lop^.next;
            lop^.next := str;
            end; {else}
         lop := str;
         end; {if}
   end; {CreateTemps}


begin {CommonSubexpressionElimination}
temps := nil;				{no temps allocated, yet}
repeat					{identify common parts}
   done := true;
   bb := DAGblocks;
   while bb <> nil do begin
      Spin;
      op := bb^.code;
      if op <> nil then begin
	 CheckTree(bb^.code, bb);
	 while op^.next <> nil do begin
            CheckTree(op^.next, bb);
            if op^.next <> nil then
	       op := op^.next;
	    end; {while}
	 end; {if}
      bb := bb^.next;
      end; {while}
until done;
bb := DAGblocks;			{count the number of parents}
while bb <> nil do begin
   op := bb^.code;
   Spin;
   while op <> nil do begin
      CountParents(op);
      op := op^.next;
      end; {while}
   bb := bb^.next;
   end; {while}
bb := DAGblocks;			{check for blocked instructions}
while bb <> nil do begin
   op := bb^.code;
   Spin;
   while op <> nil do begin
      CheckForBlocks(op);
      op := op^.next;
      end; {while}
   bb := bb^.next;
   end; {while}
bb := DAGblocks;			{create temps for common subexpressions}
while bb <> nil do begin
   op := bb^.code;
   lop := nil;
   ResetTemps;
   Spin;
   while op <> nil do begin
      CreateTemps(op, bb, lop);
      lop := op;
      op := op^.next;
      end; {while}
   bb := bb^.next;
   end; {while}
DisposeTemps;				{get rid of the temp variable list}
end; {CommonSubexpressionElimination}

{- Loop Optimizations ------------------------------------------}

procedure AddOperation (op: icptr; var lp: iclist);

{ Add an operation to an operation list				}
{								}
{ parameters:							}
{    op - operation to add					}
{    lp - list to add the operation to				}

var
   inList: boolean;			{is op already in the list?}
   llp: iclist;				{work pointer}

begin {AddOperation}
llp := lp;
inList := false;
while llp <> nil do
   if MatchLoc(llp^.op, op) then begin
      inList := true;
      llp := nil;
      end {if}
   else
      llp := llp^.next;
if not inList then begin
   new(llp);
   llp^.next := lp;
   lp := llp;
   llp^.op := op;
   end; {if}
end; {AddOperation}


procedure DisposeBlkList (var blk: blockListPtr);

{ dispose of all entries in the block list			}
{								}
{ parameters:							}
{    blk - list of blocks to dispose of				}

var
   bk1, bk2: blockListPtr;		{work pointers}

begin {DisposeBlkList}
bk1 := blk;
blk := nil;
while bk1 <> nil do begin
   bk2 := bk1;
   bk1 := bk2^.next;
   dispose(bk2);
   end; {while}
end; {DisposeBlkList}


procedure DisposeOpList (var oplist: iclist);

{ dispose of all entries in the list				}
{								}
{ parameters:							}
{    oplist - operation list to dispose of			}

var
   op1, op2: iclist;			{work pointers}

begin {DisposeOpList}
op1 := oplist;
oplist := nil;
while op1 <> nil do begin
   op2 := op1;
   op1 := op2^.next;
   dispose(op2);
   end; {while}
end; {DisposeOpList}


procedure DumpLoopLists;

{ dispose of lists created by ReachingDefinitions and Dominators}

var
   bb: blockPtr;			{used to trace basic block list}
   dom: blockListPtr;			{used to dispose of a dominator}
   
begin {DumpLoopLists}
bb := DAGBlocks;
while bb <> nil do begin
   DisposeOpList(bb^.c_in);		{dump the reaching definition lists}
   DisposeOpList(bb^.c_out);
   DisposeOpList(bb^.c_gen);
   DisposeBlkList(bb^.dom);
   while bb^.dom <> nil do begin	{dump the dominator lists}
      dom := bb^.dom;
      bb^.dom := dom^.next;
      dispose(dom);
      end; {while}
   bb := bb^.next;
   end; {while}
end; {DumpLoopLists}


procedure AddLoads (jp: icptr; var lp: iclist);

{ Add any load addresses from the children of this		}
{ operation							}
{								}
{ parameters:							}
{    jp - operation to check					}
{    lp - list to add the loads to				}

begin {AddLoads}
if jp^.opcode in [pc_lda,pc_lao,pc_lod,pc_lod] then
   AddOperation(jp, lp)
else begin
   if jp^.left <> nil then
      AddLoads(jp^.left, lp);
   if jp^.right <> nil then
      AddLoads(jp^.right, lp);
   end {else}
end; {AddLoads}


procedure FlagIndirectUses;

{ Find all variables that could be changed by an indirect	}
{ access.							}

var
   bb: blockPtr;			{used to trace block list}


   procedure Check (op: icptr; doingInd: boolean);

   { Check op and its children & followers for dangerous	}
   { references							}
   {								}
   { parameters:						}
   {    op - operation to check					}
   {    doingInd - are we doing a pc_ind?  If so, pc_lda's	}
   {       are safe						}

   var
      lDoingInd: boolean;		{local doingInd}

   begin {Check}
   while op <> nil do begin
      if op^.opcode = pc_ind then
         lDoingInd := true
      else
         lDoingInd := doingInd;
      if op^.left <> nil then
         Check(op^.left, lDoingInd);
      if op^.right <> nil then
         Check(op^.right, lDoingInd);
      if op^.opcode in [pc_lao,pc_cpo,pc_ldo,pc_sro,pc_gil,pc_gli,
         pc_gdl,pc_gld] then
         AddOperation(op, c_ind)
      else if op^.opcode = pc_ind then begin
         if op^.left^.opcode = pc_ind then
            AddLoads(op^.left^.left, c_ind);
         end {else if}
      else if op^.opcode = pc_lda then
         if not doingInd then
            AddOperation(op, c_ind);
      op := op^.next;
      end; {while}
   end; {Check}


begin {FlagIndirectUses}
c_ind := nil;
bb := DAGBlocks;
while bb <> nil do begin
   Check(bb^.code, false);
   bb := bb^.next;
   end; {while}
end; {FlagIndirectUses}


procedure DoLoopOptimization;

{ Perform optimizations related to loops and data flow		}

type
   dftptr = ^dftrecord;			{depth first tree edges}
   dftrecord = record
      next: dftptr;
      from, dest: blockPtr;
      end;

var
   backEdge: dftptr;			{list of back edges}
   dft: dftptr;				{depth first tree}
   dft2: dftptr;			{work pointer}


   function DFN (i: integer): blockPtr;

   { find the basic block with dfn index of i			}
   {								}
   { parameters:						}
   {    i - index to look for					}
   {								}
   { Returns: block pointer, or nil if there is none		}

   var
      bb: blockPtr;			{used to trace block list}

   begin {DFN}
   bb := DAGBlocks;
   DFN := nil;
   while bb <> nil do begin
      if bb^.dfn = i then begin
         DFN := bb;
         bb := nil;
         end
      else
         bb := bb^.next;
      end; {while}
   end; {DFN}


   function MemberDFNList (dfn: integer; bl: blockListPtr): boolean;

   { See if dfn is a member of the list bl			}
   {								}
   { parameters:						}
   {    dfn - block number to check				}
   {    bl - list of block numbers to check			}
   {								}
   { Returns: True if dfn is in bl, else false.			}

   begin {MemberDFNList}
   MemberDFNList := false;
   while bl <> nil do
      if bl^.dfn = dfn then begin
         MemberDFNList := true;
         bl := nil;
         end {if}
      else
         bl := bl^.next;
   end; {MemberDFNList}


   function FindDAG (q: integer): blockPtr;

   { Find the DAG containing label q				}
   {								}
   { parameters:						}
   {    q - label to find					}
   {								}
   { Returns: pointer to the proper basic block			}

   var
      bb: blockPtr;			{used to trace basic block list}

   begin {FindDAG}
   bb := DAGBlocks;
   FindDAG := nil;
   while bb <> nil do begin
      if bb^.code^.opcode = dc_lab then
         if bb^.code^.q = q then begin
            FindDAG := bb;
            bb := nil;
            end; {if}
      if bb <> nil then
         bb := bb^.next;
      end; {while}
   end; {FindDAG}


   procedure DepthFirstOrder;

   { Number the DAG for depth first order			}

   var
      bb: blockPtr;			{used to trace basic block lists}
      i: integer;			{dfn index}


      procedure Search (bb: blockPtr);

      { Search this block					}
      {								}
      { parameters:						}
      {    bb - basic block to search				}

      var
         blk: blockPtr;			{work block}
         ndft: dftptr;			{for new tree entries}
         op: icptr;			{used to trace operation list}


         function NotUnconditional: boolean;

         { See if the block ends with something other than an	}
         { unconditional jump					}
         {							}
         { Returns: True if the block ends with something other	}
         {    than pc_ujp or pc_add, else false			}

         var
            op: icptr;			{used to trace the list}

         begin {NotUnconditional}
         NotUnconditional := true;
         op := bb^.code;
         if op <> nil then begin
            while op^.next <> nil do
               op := op^.next;
            if op^.opcode in [pc_add,pc_ujp] then
               NotUnconditional := false;
            end; {if}
         end; {NotUnconditional}


      begin {Search}
      Spin;
      if bb <> nil then
	 if not bb^.visited then begin
	    bb^.visited := true;
            if NotUnconditional then
               if bb^.next <> nil then begin
                  new(ndft);
                  ndft^.next := dft;
                  dft := ndft;
                  ndft^.from := bb;
                  ndft^.dest := bb^.next;
                  Search(bb^.next);
                  end; {if}
	    op := bb^.code;
	    while op <> nil do begin
               if op^.opcode in [pc_ujp, pc_fjp, pc_tjp, pc_add] then begin
                  blk := FindDAG(op^.q);
                  new(ndft);
                  if blk^.visited then begin
                     ndft^.next := backEdge;                        
                     backEdge := ndft;
                     end {if}
                  else begin
                     ndft^.next := dft;
                     dft := ndft;
        	     Search(blk);
                     end; {else}
                  ndft^.from := bb;
                  ndft^.dest := blk;
                  end; {if}
               op := op^.next;
               end; {while}
            bb^.dfn := i;
            i := i-1;
            end; {if}
      end; {Search}


   begin {DepthFirstOrder}
   dft := nil;
   backEdge := nil;
   i := 0;
   bb := DAGblocks;
   while bb <> nil do begin
      bb^.visited := false;
      i := i+1;
      bb := bb^.next;
      end; {while}
   Search(DAGBlocks);
   if i <> 0 then begin                 {ensure DFNs start from 1}
      bb := DAGblocks;
      while bb <> nil do begin
         if bb ^.dfn <> 0 then
            bb^.dfn := bb^.dfn - i;
         bb := bb^.next;
         end; {while}
      end; {if}
   end; {DepthFirstOrder}


   procedure AddDominator (var dom: blockListPtr; dfn: integer);

   { Add dfn to the list of dominators				}
   {								}
   { parameters:						}
   {    dom - dominator list					}
   {    dfn - new dominator number				}

   var
      dp: blockListPtr;			{new node}

   begin {AddDominator}
   new(dp);
   dp^.last := nil;
   dp^.next := dom;
   if dom <> nil then
      dom^.last := dp;
   dom := dp;
   dp^.dfn := dfn;
   end; {AddDominator}
      

   procedure Dominators;

   { Find a list of dominators for each node			}

   var
      bb: blockPtr;			{used to trace the block list}
      change: boolean;			{for loop termination test}
      i, j: integer;			{loop variables}
      maxdfn, mindfn: integer;		{max and min dfn values used}


      procedure CheckPredecessors (bb: blockPtr; bl: dftptr);

      { Eliminate nodes that don't dominate a predecessor	}
      {								}
      { parameters:						}
      {    bb - block being checked				}
      {    bl - list of edges to check for predecessors		}

      var
         dp: blockListPtr;		{list of dominator numbers}
         tdp: blockListPtr;		{used to remove a dominator entry}

      begin {CheckPredecessors}
      while bl <> nil do begin
         if bl^.dest = bb then begin
            dp := bb^.dom;
            while dp <> nil do
               if dp^.dfn <> bb^.dfn then
                  if not MemberDFNList(dp^.dfn, bl^.from^.dom) then begin
                     change := true;
                     tdp := dp;
                     if tdp^.last = nil then
                        bb^.dom := tdp^.next
                     else
                        tdp^.last^.next := tdp^.next;
                     if tdp^.next <> nil then
                        tdp^.next^.last := tdp^.last;
                     dp := tdp^.next;
                     dispose(tdp);
                     end {if}
                  else
                     dp := dp^.next
               else
                  dp := dp^.next;
            end; {if}
         bl := bl^.next;
         end; {while}
      end; {CheckPredecessors}


   begin {Dominators}
   Spin;
   maxdfn := 0;				{find the largest dfn}
   bb := DAGBlocks;
   while bb <> nil do begin
      if bb^.dfn > maxdfn then
         maxdfn := bb^.dfn;
      bb := bb^.next;
      end; {while}
   AddDominator(DAGBlocks^.dom, DAGBlocks^.dfn); {the first node is it's own dominator}
   mindfn := DAGBlocks^.dfn;		{assume all other nodes are dominated by every other node}
   for i := mindfn+1 to maxdfn do begin
      bb := DFN(i);
      if bb <> nil then
         for j := mindfn to maxdfn do
            AddDominator(bb^.dom, j);
      end; {for}
   repeat				{iterate to the true set of dominators}
      change := false;
      for i := mindfn+1 to maxdfn do begin
         bb := DFN(i);
         CheckPredecessors(bb, dft);
         CheckPredecessors(bb, backEdge);
         end; {for}
   until not change;
   end; {Dominators}


   procedure ReachingDefinitions;

   { find the list of reaching definitions for each basic block	}

   var
      bb: blockPtr;			{block being scanned}
      change: boolean;			{loop termination test}
      i: integer;			{node index number}
      newIn: iclist;			{list of inputs}


      function Gen (op: icptr): iclist;

      { find a list of generated values				}
      {								}
      { parameters:						}
      {    op - list of intermediate codes to scan		}
      {								}
      { Returns: list of generated definitions			}

      var
         gp: iclist;			{list of generated definitions}
         indFound: boolean;		{has an indirect store been found?}


         procedure Check (ip: icptr);

         { Add any result from ip to gp				}
         {							}
         { parameters:						}
         {    ip - instruction to check				}

         var
            lc_ind: iclist;		{used to trace the c_ind list}

         begin {Check}
         if ip^.left <> nil then
            Check(ip^.left);
         if ip^.right <> nil then
            Check(ip^.right);
         if ip^.opcode in
            [pc_str,pc_sro,pc_cop,pc_cpo,pc_lli,pc_lil,pc_lld,pc_ldl,
             pc_gli,pc_gil,pc_gld,pc_gdl] then
            AddOperation(ip, gp)
         else if ip^.opcode in [pc_mov,pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild] then
            AddLoads(ip, gp);
         if not indFound then
            if ip^.opcode in
               [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,pc_cup,pc_cui,pc_tl1]
                then begin
               lc_ind := c_ind;
               while lc_ind <> nil do begin
                  AddOperation(lc_ind^.op, gp);
                  lc_ind := lc_ind^.next;
                  end; {while}
               indFound := true;
               end; {if}
         end; {Check}                                         


      begin {Gen}
      indFound := false;
      gp := nil;
      while op <> nil do begin
         Check(op);
         op := op^.next;
         end; {while}
      Gen := gp;
      end; {Gen}


      function EqualSets (l1, l2: iclist): boolean;

      { See if two sets of stores and copies are equivalent	}
      {								}
      { parameters:						}
      {    l1, l2 - lists of copies and stores			}
      {								}
      { Returns: True if the lists are equivalent, else false	}
      {								}
      { Notes: The members of each list are assumed to be	}
      {    unique within that list.				}

      var
         c1, c2: integer;		{number of elements in the sets}
         l3: iclist;			{used to trace the lists}
         matchFound: boolean;		{was a match found?}

      begin {EqualSets}
      EqualSets := false;		{assume they are not equal}
      c1 := 0;				{count the elements of l1}
      l3 := l1;
      while l3 <> nil do begin
         c1 := c1+1;
         l3 := l3^.next;
         end; {while}
      c2 := 0;				{count the elements of l2}
      l3 := l2;
      while l3 <> nil do begin
         c2 := c2+1;
         l3 := l3^.next;
         end; {while}
      if c1 = c2 then begin		{make sure each member of l1 is in l2}
         EqualSets := true;
         while l1 <> nil do begin
            matchFound := false;
            l3 := l2;
            while l3 <> nil do begin
               if MatchLoc(l1^.op, l3^.op) then begin
                  l3 := nil;
                  matchFound := true;
                  end {if}
               else
                  l3 := l3^.next;
               end; {while}
            if not matchFound then begin
               EqualSets := false;
               l1 := nil;
               end {if}
            else
               l1 := l1^.next;
            end; {while}
         end; {if}
      end; {EqualSets}


      function Union (l1, l2: iclist): iclist;

      { Returns a list that is the union of two input lists	}
      {								}
      { parameters:						}
      {    l1, l2 - lists					}
      {								}
      { Returns: New, dynamically allocated list that includes	}
      {    all of the members in l1 and l2.			}
      {								}
      { Notes:							}
      {    1.  If there are duplicates, the member from l1 is	}
      {        returned.					}
      {    2.  It is assumed that all members of l1 and l2 are	}
      {        unique within their own list.			}
      {    3.  The original lists are not disturbed.		}
      {    4.  The caller is responsible for disposing of the	}
      {        memory used by the list.				}

      var
         lp: iclist;			{new list pointer}
         np: iclist;			{new list member pointer}
         tp: iclist;			{temp list pointer}

      begin {Union}
      lp := nil;
      tp := l1;
      while tp <> nil do begin
         new(np);
         np^.next := lp;
         lp := np;
         np^.op := tp^.op;
         tp := tp^.next;
         end; {while}
      while l2 <> nil do begin
         if not Member(l2^.op, l1) then begin
            new(np);
            np^.next := lp;
            lp := np;
            np^.op := l2^.op;
            end; {if}
         l2 := l2^.next;
         end; {while}
      Union := lp;
      end; {Union}


      function UnionOfPredecessors (bptr: blockPtr): iclist;

      { create a union of the outputs of predecessors to bptr	}
      {								}
      { parameters:						}
      {    bptr - block for which to look for predecessors	}
      {								}
      { Returns: Resulting set					}

      var
         bp: dftptr;			{used to trace edge lists}
         plist: iclist;			{result list}
         tlist: iclist;			{temp result list}

      begin {UnionOfPredecessors}
      plist := nil;
      bp := dft;
      while bp <> nil do begin
         if bp^.dest = bptr then begin
            tlist := Union(plist, bp^.from^.c_out);
            DisposeOpList(plist);
            plist := tlist;
            end; {if}
         bp := bp^.next;
         end; {while}
      bp := backEdge;
      while bp <> nil do begin
         if bp^.dest = bptr then begin
            tlist := Union(plist, bp^.from^.c_out);
            DisposeOpList(plist);
            plist := tlist;
            end; {if}
         bp := bp^.next;
         end; {while}
      UnionOfPredecessors := plist;
      end; {UnionOfPredecessors}


   begin {ReachingDefinitions}
   i := 1;				{initialize the lists}
   repeat
      bb := DFN(i);
      if bb <> nil then begin
         bb^.c_in := nil;
         bb^.c_gen := Gen(bb^.code);
         bb^.c_out := Union(nil, bb^.c_gen);
         end; {if}
      i := i+1;
   until bb = nil;
   repeat				{iterate to a solution}
      change := false;
      i := 1;
      repeat
	 Spin;
         bb := DFN(i);
         if bb <> nil then begin
            newIn := UnionOfPredecessors(bb);
            if not EqualSets(bb^.c_in, newIn) then begin
               {IN[n] := newIn}
               DisposeOpList(bb^.c_in);
               bb^.c_in := newIn;
               newIn := nil;
               {OUT[n] := IN[n] - KILL[n] U GEN[n]}
               DisposeOpList(bb^.c_out);
               bb^.c_out := Union(bb^.c_in, nil);
               change := true;
               end; {if}
            DisposeOpList(newIn);
            end; {if}
         i := i+1;
      until bb = nil;
   until not change;
   end; {ReachingDefinitions}


   procedure LoopInvariantRemoval;

   { Remove all loop invariant computations			}

   type
      loopPtr = ^loopRecord;		{blocks in a list}
      loopRecord = record
	 next: loopPtr;			{next entry}
	 block: blockPtr;		{code block}
         exit: boolean;			{is this a loop exit?}
	 end;

      loopListPtr = ^loopListRecord;	{list of loop lists}
      loopListRecord = record
	 next: loopListPtr;
	 loop: loopPtr;
	 end;

   var
      icount: integer;			{order invariant found}
      loops: loopListPtr;		{list of loops}
      lp: loopPtr;			{used to trace loop lists}
      llp: loopListPtr;			{used to trace the list of loops}
      fakeDFN: integer;			{to uniquely number newly-created blocks}


      function InLoop (blk: blockPtr; lp: loopPtr): boolean;

      { See if the block is in the loop				}
      {								}
      { parameters:						}
      {    blk - block to check for				}
      {    lp - loop list					}
      {								}
      { Returns: True if blk is in the list, else false		}

      begin {InLoop}
      InLoop := false;
      while lp <> nil do begin
         if lp^.block = blk then begin
            lp := nil;
            InLoop := true;
            end {if}
         else
            lp := lp^.next;
         end; {while}
      end; {InLoop}


      procedure FindLoops;

      { Create a list of the natural loops			}

      var
         blk: blockPtr;			{target block for a jump}
	 bp: dftptr;			{used to trace the back edges}
         lp, lp2: loopPtr;		{used to reverse the list}
	 llp: loopListPtr;		{loop list header entry}
	 llp2: loopListPtr;		{used to reverse the list}
         op: icptr;			{used to trace the opcode list}


	 procedure Add (block: blockPtr);

	 { Add a block to the current loop list				}
	 {								}
	 { parameters:							}
	 {    block - block to add					}

	 var
            lp: loopPtr;			{new loop entry}

	 begin {Add}
	 new(lp);
	 lp^.next := llp^.loop;
	 llp^.loop := lp;
	 lp^.block := block;
         lp^.exit := false;
	 end; {Add}


	 procedure Insert (block: blockPtr);

	 { Insert a block into the loop list				}
	 {								}
	 { parameters:							}
	 {    block - block to add					}


            procedure AddPredecessors (block: blockPtr; bl: dftptr);

            { add any predecessors to the loop				}
            {								}
            { parameters:						}
            {    block - block for which to check for			}
            {       predecessors					}
            {    bl - list of edges to check				}

            begin {AddPredecessors}
            while bl <> nil do begin
               if bl^.dest = block then
        	  Insert(bl^.from);
               bl := bl^.next;
               end; {while}
            end; {AddPredecessors}


	 begin {Insert}
	 if not InLoop(block, llp^.loop) then begin
            Add(block);
            AddPredecessors(block, dft);
            AddPredecessors(block, backEdge);
            end; {if}
	 end; {Insert}


      begin {FindLoops}
      loops := nil;
      bp := backEdge;			{scan the back edges}
      while bp <> nil do begin
	 if MemberDFNList(bp^.dest^.dfn, bp^.from^.dom) then begin
            new(llp);			{create a new loop list entry}
            llp^.next := loops;
            loops := llp;
            llp^.loop := nil;
            Add(bp^.dest);
            Insert(bp^.from);
            lp := llp^.loop;		{reverse the list}
            llp^.loop := nil;
            while lp <> nil do begin
               lp2 := lp;
               lp := lp2^.next;
               lp2^.next := llp^.loop;
               llp^.loop := lp2;
               end; {while}
            lp := llp^.loop;		{mark the exits}
            while lp <> nil do begin
               op := lp^.block^.code;
               while op <> nil do begin
        	  if op^.opcode in [pc_ujp, pc_fjp, pc_tjp, pc_add] then begin
                     blk := FindDAG(op^.q);
                     if not InLoop(blk, llp^.loop) then
                        lp^.exit := true;
                     if op^.opcode in [pc_fjp,pc_tjp] then
                        if not InLoop(lp^.block^.next, llp^.loop) then
                           lp^.exit := true;
                     end; {if}
                  op := op^.next;                        
                  end; {while}
               lp := lp^.next;
               end; {while}
            end; {if}
	 bp := bp^.next;
	 end; {while}
      llp := loops;			{reverse the loop list}
      loops := nil;
      while llp <> nil do begin
	 llp2 := llp;
	 llp := llp2^.next;
	 llp2^.next := loops;
	 loops := llp2;
	 end; {while}
      end; {FindLoops}


      function MarkInvariants (lp: loopPtr): boolean;

      { Make a pass over the opcodes, marking those that are	}
      { invariant.						}
      {								}
      { parameters:						}
      {    lp - loop to scan					}
      {								}
      { Returns: True if any new nodes were marked, else false.	}

      var
         count: integer;		{number of generating blocks}
         indirectStores: boolean;	{does the loop contain indirect stores or function calls?}
         inhibit: boolean;		{inhibit stores?}
         lp2: loopPtr;			{used to trace the loop}
         op: icptr;			{used to trace the instruction list}
         opcode: pcodes;		{op^.opcode; for efficiency}


         procedure Check (op: icptr; olp: loopPtr);

         { See if this node or its children is invariant		}
         {								}
         { parameters:							}
         {    op - node to check					}
         {    olp - loop entry for the block containing the store	}

         var
            invariant: boolean;		{are the operands invariant?}


            function IndirectInhibit (op: icptr): boolean;

            { See if a store should be inhibited due to indirect	}
            { accesses							}
            {								}
            { parameters:						}
            {    op - instruction to check				}
            {								}
            { Returns: True if the instruction should be inhibited,	}
            {    else false.						}

            begin {IndirectInhibit}
            IndirectInhibit := false;
            if indirectStores then
               if Member(op, c_ind) then
                  IndirectInhibit := true;
            end; {IndirectInhibit}


            function NoOtherStoresOrUses (lp, olp: loopPtr; op: icptr): boolean;

            { Check for invalid stores					}
            {								}
            { parameters:						}
            {    lp - loop to check					}
            {    olp - loop entry for the block containing the store	}
            {    op - store to check					}
            {								}
            { Returns: True if the store is valid, false if not.	}
            {								}
            { Notes: Specifically, these two rules are enforced:	}
            {    1. No other stores to the same location appear in the	}
            {       loop.						}
            {    2. All uses of the value in the loop can be reached	}
            {       only by the assign.					}

            var
               lp2: loopPtr;			{used to trace the loop list}
               op2: icptr;			{used to trace code list}


               function SafeLoad (sop, lop: icptr; sbk, lbk: blockPtr): boolean;

               { See if a load is in a safe position			}
               {							}
               { parameters:						}
               {    sop - save opcode that may need to be left in loop	}
               {    lop - load operation that may inhibit the save	}
               {    sbk - block containing the save			}
               {    lbk - block containing the load			}


                  function First (op1, op2, stream: icptr): icptr;

                  { See which operation comes first				}
                  {								}
                  { parmeters:							}
                  {    op1, op2 - instructions to check				}
                  {    stream - start of block containing the instructions	}
                  {								}
                  { Returns: First operation found, or nil if missing		}

                  var
                     op: icptr;				{temp opcode}

                  begin {First}
                  if stream = op1 then
                     First := op1
                  else if stream = op2 then
                     First := op2
                  else begin
                     op := nil;
                     if stream^.left <> nil then
                        op := First(op1, op2, stream^.left);
                     if op = nil then
                        if stream^.right <> nil then
                           op := First(op1, op2, stream^.right);
                     if op = nil then
                        if stream^.next <> nil then
                           op := First(op1, op2, stream^.next);
                     First := op;
                     end; {else}     
                  end; {First}


               begin {SafeLoad}
               if sbk = lbk then
                  SafeLoad := First(sop, lop, sbk^.code) = sop
               else
                  SafeLoad := MemberDFNList(sbk^.dfn, lbk^.dom);
               end; {SafeLoad}


               function MatchStores (op, tree: icptr; opbk, treebk: blockPtr):
                  boolean;

               { Check the tree for stores to the same location as op	}
               {							}
               { parameters:						}
               {    op - store to check for				}
               {    tree - operation tree to check			}
               {    opbk - block containing op				}
               {    treebk - block containing tree			}
               {							}
               { Returns: True if there are matching stores, else false	}

               var
                  result: boolean;		{function result}

               begin {MatchStores}
               result := false;
               if tree^.opcode in [pc_lli,pc_lil,pc_lld,pc_ldl,pc_str,pc_cop,
                  pc_sro,pc_cpo,pc_gli,pc_gil,pc_gld,pc_gdl] then begin
                  if tree <> op then 
                     result := MatchLoc(op, tree);
                  end {if}
               else if tree^.opcode in [pc_ldo,pc_lod] then
                  if MatchLoc(op, tree) then
                     result := not SafeLoad(op, tree, opbk, treebk);
               if not result then
                  if tree^.left <> nil then
                     result := MatchStores(op, tree^.left, opbk, treebk);
               if not result then
                  if tree^.right <> nil then
                     result := MatchStores(op, tree^.right, opbk, treebk);
               MatchStores := result;
               end; {MatchStores}


            begin {NoOtherStoresOrUses}
            NoOtherStoresOrUses := true;
            lp2 := lp;
            while lp2 <> nil do begin
               op2 := lp2^.block^.code;
               while op2 <> nil do
                  if MatchStores(op, op2, olp^.block, lp2^.block) then begin
                     op2 := nil;
                     lp2 := nil;
                     NoOtherStoresOrUses := false;
                     end {if}
                  else
                     op2 := op2^.next;
               if lp2 <> nil then
                  lp2 := lp2^.next;
               end; {while}
            end; {NoOtherStoresOrUses}


            function NumberOfGens (op: icptr; lp: loopPtr): integer;

            { Count the number of nodes that generate op		}
            {								}
            { parameters:						}
            {    op - instruction to check				}
            {    lp - loop to check					}

            var
               count: integer;			{number of generators}

            begin {NumberOfGens}
            count := 0;
            while lp <> nil do begin
               if Member(op, lp^.block^.c_gen) then
                  count := count+1;
               lp := lp^.next;
               end; {while}
            NumberOfGens := count;
            end; {NumberOfGens}


            function PreviousStore (op, list: icptr): boolean;

            { See if the last save was invariant			}
            {								}
            { parameters:						}
            {    op - load operation					}
            {    list - block containing the load			}
            {								}
            { Returns: True if the previous store was invariant, else	}
            {    false.							}

            var
               indop: icptr;			{any indirect operation after strop}
               strop: icptr;			{last matching store before op}


               procedure Check (lop: icptr);

               { Stop if this is lop; save if it is a matching store	}
               {							}
               { parameters:						}
               {    lop - check this operation and it's children	}

               begin {Check}
               if lop^.left <> nil then
                  Check(lop^.left);
               if list <> nil then
                  if lop^.right <> nil then
                     Check(lop^.right);
               if list <> nil then
                  if lop = op then
                     list := nil
                  else if (lop^.opcode in [pc_str,pc_cop,pc_str,pc_cop])
                     and MatchLoc(op, lop) then begin
                     strop := lop;
                     indop := nil;
                     end {else if}
                  else if op^.opcode in
                     [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,pc_cup,pc_cui,pc_tl1]
                     then
                     indop := op;
               end; {Check}


               function Inhibit (indop, op: icptr): boolean;

               { See if op should be inhibited due to indirect stores	}
               {							}
               { parameters:						}
               {    indop - inhibiting indirect store or nil		}
               {    op - instruction to check				}

               begin {Inhibit}
               Inhibit := false;
               if indop <> nil then
                  if Member(op, c_ind) then
                     Inhibit := true;
               end; {Inhibit}


            begin {PreviousStore}
            indop := nil;
            strop := nil;
            while list <> nil do begin
               Check(list);
               if list <> nil then
                  list := list^.next;
               end; {while}
            PreviousStore := false;
            if strop <> nil then
               if strop^.parents <> 0 then
                  if not Inhibit(indop, op) then
                     PreviousStore := true;
            end; {PreviousStore}


         begin {Check}
         if op^.parents = 0 then begin
            invariant := true;
            if op^.left <> nil then begin
               Check(op^.left, olp);
               if op^.left^.parents = 0 then
        	  invariant := false;
               end; {if}
            if op^.right <> nil then begin
               Check(op^.right, olp);
               if op^.right^.parents = 0 then
        	  invariant := false;
               end; {if}
            if invariant then begin
               opcode := op^.opcode;
               if opcode in
        	  [pc_adi,pc_adl,pc_adr,pc_and,pc_lnd,pc_bnd,pc_bal,
                   pc_bnt,pc_bnl,pc_bor,pc_blr,pc_bxr,pc_blx,pc_bno,
                   pc_dec,pc_dvi,pc_udi,pc_dvl,pc_udl,pc_dvr,pc_equ,pc_neq,
                   pc_grt,pc_les,pc_geq,pc_leq,pc_inc,pc_ior,pc_lor,
                   pc_ixa,pc_lad,pc_lao,pc_lca,pc_lda,pc_ldc,pc_mod,pc_uim,
                   pc_mdl,pc_ulm,pc_mpi,pc_umi,pc_mpl,pc_uml,pc_mpr,pc_ngi,
                   pc_ngl,pc_ngr,pc_not,pc_pop,pc_sbi,pc_sbl,pc_sbr,
                   pc_shl,pc_sll,pc_shr,pc_usr,pc_slr,pc_vsr,pc_tri,
                   pc_bqr,pc_bqx,pc_baq,pc_bnq,pc_ngq,pc_adq,pc_sbq,
                   pc_mpq,pc_umq,pc_dvq,pc_udq,pc_mdq,pc_uqm,pc_rbo]
                  then begin
        	  op^.parents := icount;
                  icount := icount+1;
                  end {if}
               else if opcode = pc_ind then begin
                  {conservatively assume any indirect stores may alias with op}
                  if not indirectStores then begin
                     op^.parents := icount;
                     icount := icount+1;
                     end; {if}
                  end {else if}
               else if opcode = pc_cnv then begin
        	  if op^.q & $000F <> ord(cgVoid) then begin
        	     op^.parents := icount;
                     icount := icount+1;
                     end; {if}
                  end {else if}
               else if opcode
                  in [pc_sro,pc_sto,pc_str,pc_cop,pc_cpo,pc_cpi]
                  then begin
                  if not inhibit then
                     if not IndirectInhibit(op) then
                	if NoOtherStoresOrUses(lp, olp, op) then begin
        		   op^.parents := icount;
                	   icount := icount+1;
                	   end; {if}
                  end {else if}
               else if opcode in [pc_ldo,pc_lod] then begin
        	  {invariant if there is an immediately preceding invariant store}
        	  if PreviousStore(op, lp2^.block^.code) then begin
                     op^.parents := icount;
                     icount := icount+1;
                     end {if}
        	  else if not Member(op, lp2^.block^.c_gen) then begin
                     {invariant if there are no generators in the loop}
                     count := NumberOfGens(op, lp);
                     if count = 0 then begin
                        op^.parents := icount;       
                        icount := icount+1;
                        end {if}
                     else if count = 1 then begin
                	{invariant if there is one generator AND the generator}
                        {is not in the current block AND no reaching          }
                        {definitions for the loop AND generating statement is }
                        {invariant                                            }
                	if memberOp^.parents <> 0 then
                           if not Member(op, lp^.block^.c_in) then begin
                	      op^.parents := icount;
                	      icount := icount+1;
                              end; {if}
                	end; {else if}
                     end; {else}
        	  end {else if}
               end; {if}
            if op^.parents <> 0 then
               MarkInvariants := true;
            end; {if}
         end; {Check}


         function CheckForIndirectStores (lp: loopPtr): boolean;

         { See if there are any indirect stores or function calls in	}
         { the loop							}
         {								}
         { parameters:							}
         {    lp - loop to check					}
         {								}
         { Returns: True if there are indirect stores or function	}
         {    calls, else false.					}


            function CheckOps (op: icptr): boolean;

            { Check this operation list					}
            {								}
            { parameters:						}
            {    op - operation list to check				}
            {								}
            { Returns: True if an indirect store or function call is	}
            {    found, else false.					}

            var
               result: boolean;			{value to return}

            begin {CheckOps}
            result := false;
            while op <> nil do begin
               if op^.opcode in
                  [pc_sto,pc_cpi,pc_iil,pc_ili,pc_idl,pc_ild,pc_cup,pc_cui,
                   pc_tl1,pc_mov]
                  then begin
                  result := true;
                  op := nil;
                  end {if}
               else begin
                  if op^.left <> nil then
                     result := CheckOps(op^.left);
                  if not result then
                     if op^.right <> nil then
                        result := CheckOps(op^.right);
                  if result then
                     op := nil;
                  end; {if}
               if op <> nil then
                  op := op^.next;
               end; {while}
            CheckOps := result;
            end; {CheckOps}


         begin {CheckForIndirectStores}
         CheckForIndirectStores := false;
         while lp <> nil do
            if CheckOps(lp^.block^.code) then begin
               CheckForIndirectStores := true;
               lp := nil;
               end {if}
            else
               lp := lp^.next;
         end; {CheckForIndirectStores}


         function DominatesExits (dfn: integer; lp: loopPtr): boolean;

         { See if this block dominates all loop exits			}
         {								}
         { parameters:							}
         {    dfn - block that must dominate exits			}
         {    lp - loop list						}
         {								}
         { Returns: True if the block dominates all exits, else false.	}

         var
            dom: blockListPtr;			{used to trace dominator list}

         begin {DominatesExits}
         DominatesExits := true;
         while lp <> nil do begin
            if lp^.exit then begin
               dom := lp^.block^.dom;
               while dom <> nil do 
                  if dom^.dfn = dfn then
                     dom := nil
                  else begin
                     dom := dom^.next;
                     if dom = nil then begin
                        lp := nil;
                        DominatesExits := false;
                        end; {if}
                     end; {else}
               end; {if}
            if lp <> nil then
               lp := lp^.next;
            end; {while}
         end; {DominatesExits}


      begin {MarkInvariants}
      MarkInvariants := false;
      lp2 := lp;
      while lp2 <> nil do begin
         inhibit := not DominatesExits(lp2^.block^.dfn, lp);
         indirectStores := CheckForIndirectStores(lp);
         op := lp2^.block^.code;
         while op <> nil do begin
            Check(op, lp2);
            op := op^.next;
            end; {while}
         lp2 := lp2^.next;
         end; {while}
      end; {MarkInvariants}


      procedure RemoveInvariants (llp: loopListPtr);

      { Remove loop invariant calculations			}
      {								}
      { parameters:						}
      {    llp - pointer to the loop entry to process		}

      var
         icount, oldIcount: integer;	{invariant order counters}
         nhp: blockPtr;			{new loop header pointer}
         ohp: blockPtr;			{old loop header pointer}
         op1, op2, op3: icptr;		{used to reverse the code list}


         procedure CreateHeader;

         { Create the new loop header					}
         {								}
         { Notes: As a side effect, CreateHeader sets nhp to point to	}
         {    the new loop header, and ohp to point to the old header.	}

         var
            lp: loopPtr;			{new loop list entry}

         begin {CreateHeader}
         nhp := pointer(Calloc(sizeof(block)));	{create the new block}
         ohp := llp^.loop^.block;		{insert it in the block list}
         nhp^.last := ohp^.last;
         if nhp^.last <> nil then
            nhp^.last^.next := nhp;
         nhp^.next := ohp;
         ohp^.last := nhp;
         nhp^.dfn := fakeDFN;			{just a unique number, not a real DFN}
         fakeDFN := fakeDFN - 1;
	 new(lp);				{add it to the loop list}
	 lp^.next := llp^.loop;
	 llp^.loop := lp;
	 lp^.block := nhp;
         lp^.exit := false;
         end; {CreateHeader}


         function FindInvariant (ic: integer): integer;

         { Find the next invariant calculation				}
         {								}
         { parameters:							}
         {    ic - base count; the new count must exceed this		}
         {								}
         { Returns: count for the invariant record to remove		}

         var
            lp: loopPtr;			{used to trace loop list}
            op: icptr;				{used to trace code list}
            nic: integer;			{lowest count > ic}


            procedure Check (op: icptr);

            { See if op or its children represent a newer invariant	}
            { calculation than the one numbered nic			}
            {								}
            { parameters:						}
            {    op - instruction to check				}
            {								}
            { Notes: Rejecting pc_bno here is rather odd, but it allows	}
            {    expressions _containing_ pc_bno to be removed without	}
            {    messing up pc_tri operations by allowing pc_bno to be	}
            {    removed as the top level of an expression.		}

            begin {Check}
            if op^.parents = 0 then begin
               if op^.left <> nil then
                  Check(op^.left);
               if op^.right <> nil then
                  Check(op^.right);
               end {if}
            else begin
               if op^.parents < nic then
                  if op^.parents > ic then
                     if op^.opcode <> pc_bno then
                        nic := op^.parents;
               end; {else}
            end; {Check}


         begin {FindInvariant}
         nic := maxint;
         lp := llp^.loop;
         while (lp <> nil) and (nic <> ic+1) do begin
            op := lp^.block^.code;
            while op <> nil do begin
               Check(op);
               op := op^.next;
               end; {while}
            lp := lp^.next;
            end; {while}
         FindInvariant := nic;
         end; {FindInvariant}


         procedure RemoveInvariant (ic: integer);

         { Move the invariant calculation to the header			}
         {								}
         { parameters:							}
         {    ic - index number for instruction to remove		}

         var
            done: boolean;			{loop termination test}
            lp: loopPtr;			{used to trace loop list}
            op: icptr;				{used to trace code list}


            procedure Check (op: icptr);

            { See if a child of op is the target instruction to move	}
            { (If so, move it.)						}
            {								}
            { parameters:						}
            {    op - instruction to check				}


               procedure Remove (var op: icptr);

               { Move a calculation to the loop header			}
               {							}
               { parameters:						}
               {    op - invariant calculation to move			}

               var
		  loc, op2, str: icptr;		{new opcodes}
		  optype: baseTypeEnum;		{type of the temp variable}

               begin {Remove}
               if op^.opcode in [pc_pop,pc_str,pc_sro,pc_sto,pc_sbf] then
                  {do nothing for now - would need special code to move these}
               else if (op^.left <> nil) or (op^.right <> nil) then begin
        	  optype := TypeOf(op);		{create a temp label}
        	  loc := pointer(Calloc(sizeof(intermediate_code)));
        	  loc^.opcode := dc_loc;
        	  loc^.optype := cgWord;
        	  maxLoc := maxLoc + 1;
        	  loc^.r := maxLoc;
        	  loc^.q := TypeSize(optype);
        	  loc^.next := nhp^.code;
        	  nhp^.code := loc;
						{make a copy of the tree}
        	  op2 := pointer(Malloc(sizeof(intermediate_code)));
        	  op2^ := op^;
        	  op^.opcode := pc_lod;		{substitute a load of the temp}
        	  op^.optype := optype;
        	  op^.r := loc^.r;
        	  op^.q := 0;
        	  op^.left := nil;
        	  op^.right := nil;
						{store the temp result}
        	  str := pointer(Calloc(sizeof(intermediate_code)));
        	  str^.opcode := pc_str;
        	  str^.optype := optype;
        	  str^.r := loc^.r;
        	  {str^.q := 0;}
        	  str^.left := op2;
        	  str^.next := loc^.next;	{insert the store in the basic block}
        	  loc^.next := str;
                  end; {else if}
               done := true;
               end; {Remove}


            begin {Check}
            if op^.left <> nil then begin
               if op^.left^.parents = ic then
                  Remove(op^.left);
               if not done then
                  Check(op^.left);
               end; {if}
            if not done then
               if op^.right <> nil then begin
        	  if op^.right^.parents = ic then 
                     Remove(op^.right);
                  if not done then
                     Check(op^.right);
                  end; {if}
            end; {Check}   


            procedure RemoveTop (var op: icptr);

            { Move a top-level instruction to the header		}
            {								}
            { parameters:						}
            {    op - top level instruction to remove			}

            var
               op2: icptr;			{temp operation}

            begin {RemoveTop}
            op2 := op;
            op := op^.next;
            op2^.next := nhp^.code;
            nhp^.code := op2;
            end; {RemoveTop}


         begin {RemoveInvariant}
         lp := llp^.loop;
         done := false;
         while not done do begin
            op := lp^.block^.code;
            if op <> nil then
               if op^.parents = ic then begin
        	  RemoveTop(lp^.block^.code);
        	  done := true;
        	  end {if}
               else begin
        	  Check(op);
        	  while (op^.next <> nil) and (not done) do begin
                     if op^.next^.parents = ic then begin
                	RemoveTop(op^.next);
                	done := true;
                	end {if}
                     else
                	Check(op^.next);
                     if op^.next <> nil then
        		op := op^.next;
        	     end; {while}
        	  end; {else}
            lp := lp^.next;
            if lp = nil then
               done := true;
            end; {while}
         end; {RemoveInvariant}


         procedure AdjustControlFlow;

         { Adjust control flow to account for loop invariant removal.	}
         { The current loop's back edges should go to the old header	}
         { block, bypassing removed invariant computations.  Any other	}
         { jumps to the start of the loop should go to the new header	}
         { block so that those computations are performed.		}

         var
            lp: loopPtr;		{used to trace loop list}
            op, op1: icptr;		{used to trace code list}

         begin {AdjustControlFlow}
         				{move old header label to new header}
         				{(for any jumps to it from outside loop)}
         if (ohp^.code = nil) or (ohp^.code^.opcode <> dc_lab) then
            TermError(3);		{shouldn't happen, but let's be sure}
         op1 := pointer(Calloc(sizeof(intermediate_code)));
         op1^.opcode := dc_lab;
         op1^.q := ohp^.code^.q;
         op1^.next := nhp^.code;
         nhp^.code := op1;

         ohp^.code^.q := GenLabel;	{make new label for old header &}
         lp := llp^.loop;		{adjust loop back edges to go to it}
         while (lp <> nil) do begin
            op := lp^.block^.code;
            while op <> nil do begin
               if op^.opcode in [pc_ujp,pc_fjp,pc_tjp,pc_add] then
                  if op^.q = op1^.q then begin
                     op^.q := ohp^.code^.q;
                     end;
               op := op^.next;
               end; {while}
            lp := lp^.next;
            end; {while}
         end; {AdjustControlFlow}


         procedure UpdateLoopLists;

         { Update not-yet-processed loops to include the new header	}
         { block if appropriate.  Also update any additional loops with	}
         { the same original header to now include all the nodes of the	}
         { loop just processed, since their back edges will now go to	}
         { the new header, which dominates the original header.		}

         var
            lp, lp2, lp3: loopPtr;	{used to trace loop list}

         begin {UpdateLoopLists}
         loops := llp^.next;
         while loops <> nil do begin
            if loops^.loop^.block = ohp then begin
               				{Another loop with the same header.}
               				{Nodes of llp^.loop must be added to it.}
               				{They go after the original header.}
               lp3 := loops^.loop;
               lp := llp^.loop;
               while lp <> nil do begin
                  if lp^.block <> nhp then
                     if not InLoop(lp^.block, loops^.loop) then begin
                        new(lp2);
                        lp2^.next := lp3^.next;
                        lp2^.block := lp^.block;
                        lp2^.exit := lp^.exit;
                        lp3^.next := lp2;
                        lp3 := lp2;
                        end; {if}
                  lp := lp^.next;
                  end; {while}
               end; {if}
            lp := loops^.loop;		{Add nhp to other loops containing ohp}
            while lp <> nil do begin
               if lp^.block = ohp then begin
                  new(lp2);
                  lp2^.next := lp^.next;
                  lp2^.block := lp^.block;
                  lp2^.exit := lp^.exit;
                  lp^.next := lp2;
                  lp^.block := nhp;
                  lp^.exit := false;
                  lp := nil;
                  end {if}
               else
                  lp := lp^.next;
               end; {while}
            loops := loops^.next;
            end; {while}
         end; {UpdateLoopLists}


         procedure UpdateDominators;

         { Set dominators of the new header block, and update		}
         { dominators of other blocks to include it where appropriate.	}

         var
            bb: blockPtr;		{used to trace list of basic blocks}
            dom: blockListPtr;		{used to trace dominator list}

         begin {UpdateDominators}
         dom := ohp^.dom;		{Set dominators of new header block}
         while dom <> nil do begin
            if dom^.dfn <> ohp^.dfn then
               AddDominator(nhp^.dom, dom^.dfn);
            dom := dom^.next;
            end; {while}
         AddDominator(nhp^.dom, nhp^.dfn);

         bb := DAGBlocks;		{Add nhp to other loops' dominators}
         while bb <> nil do begin
            if MemberDFNList(ohp^.dfn, bb^.dom) then
               AddDominator(bb^.dom, nhp^.dfn);
            bb := bb^.next;
            end; {while}
         end; {UpdateDominators}


      begin {RemoveInvariants}
      CreateHeader;			{create a loop header block}
      icount := 0;			{find & remove all invariants}
      repeat
         oldIcount := icount;
         icount := FindInvariant (icount);
         if icount <> maxint then
            RemoveInvariant(icount);
      until icount = maxint;
      op1 := nhp^.code;			{reverse the new code list}
      op2 := nil;
      while op1 <> nil do begin
         op3 := op1;
         op1 := op1^.next;
         op3^.next := op2;
         op2 := op3;
         end; {while}
      nhp^.code := op2;
      					{adjust things to account for changes}
      if nhp^.code <> nil then begin
         Spin;
         AdjustControlFlow;
         UpdateLoopLists;
         UpdateDominators;
         end; {if}
      end; {RemoveInvariants}


      procedure ZeroParents (lp: loopPtr);

      { Zero the parents field in all nodes			}
      {								}
      { parameters:						}
      {    lp - loop for which to zero the parents		}

      var
         op: icptr;			{used to trace the opcode list}


         procedure Zero (op: icptr);

         { Zero the parents field for this node and its		}
         { children.						}
         {							}
         { parameters:						}
         {    op - node to zero					}

         begin {Zero}
         op^.parents := 0;
         if op^.left <> nil then
            Zero(op^.left);
         if op^.right <> nil then
            Zero(op^.right);
         end; {Zero}

            
      begin {ZeroParents}
      while lp <> nil do begin
         op := lp^.block^.code;
         while op <> nil do begin
            Zero(op);
            op := op^.next;
            end; {while}
         lp := lp^.next;
         end; {while}
      end; {ZeroParents}


   begin {LoopInvariantRemoval}
   Spin;
   FindLoops;				{find a list of natural loops}
   fakeDFN := -1;

   llp := loops;			{scan the loops...}
   icount := 1;
   while llp <> nil do begin
      Spin;
      ZeroParents(llp^.loop);		{set the parents field to zero}
      while MarkInvariants(llp^.loop) do {mark the loop invariant computations}
         ;
      if icount <> 1 then
	 RemoveInvariants(llp);		{remove loop invariant calculations}
      llp := llp^.next;
      end; {while}


   while loops <> nil do begin		{dispose of the loop lists}
      while loops^.loop <> nil do begin
	 lp := loops^.loop;
	 loops^.loop := lp^.next;
	 dispose(lp);
	 end; {while}
      llp := loops;
      loops := llp^.next;
      dispose(llp);
      end; {while}
   end; {LoopInvariantRemoval}
   

begin {DoLoopOptimization}
DepthFirstOrder;			{create the depth first tree}
ReachingDefinitions;			{find reaching definitions}
Dominators;				{find the lists of dominators}
LoopInvariantRemoval;			{remove loop invariant computations}
while dft <> nil do begin		{dispose of the depth first tree}
   dft2 := dft;
   dft := dft2^.next;
   dispose(dft2);
   end; {while}
while backEdge <> nil do begin		{dispose of the back edge list}
   dft2 := backEdge;
   backEdge := dft2^.next;
   dispose(dft2);
   end; {while}
end; {DoLoopOptimization}

{---------------------------------------------------------------}

procedure DAG {code: icptr};

{ place an op code in a DAG or tree				}
{                                                               }
{ parameters:                                                   }
{       code - opcode						}

var
   temp: icptr;				{temp node}


   procedure Generate;

   { generate the code for the current procedure		}

   var
      op: icptr;			{temp opcode pointers}


      procedure BasicBlocks;

      { Break the code up into basic blocks			}

      var
         blast: blockPtr;		{last block pointer}
         bp: blockPtr;			{current block pointer}
         cb: icptr;			{last code in block pointer}
         cp: icptr;			{current code pointer}

      begin {BasicBlocks}
      cp := DAGhead;
      DAGblocks := nil;
      if cp <> nil then begin
         bp := pointer(Calloc(sizeof(block)));
         DAGblocks := bp;
         blast := bp;
         bp^.code := cp;
         cb := cp;
         cp := cp^.next;
         cb^.next := nil;
         while cp <> nil do
					{labels start a new block}
            if cp^.opcode = dc_lab then begin
               Spin;
               bp := pointer(Calloc(sizeof(block)));
               bp^.last := blast;
               blast^.next := bp;
               blast := bp;
               bp^.code := cp;
               cb := cp;
               cp := cp^.next;
               cb^.next := nil;
               end {if}
					{conditionals are followed by a new block}
            else if cp^.opcode in [pc_fjp, pc_tjp, pc_ujp, pc_ret, pc_xjp] then
               begin
               Spin;
               while cp^.next^.opcode = pc_add do begin
                  cb^.next := cp;
        	  cb := cp;
        	  cp := cp^.next;
        	  cb^.next := nil;
                  end; {while}
               cb^.next := cp;
               cb := cp;
               cp := cp^.next;
               cb^.next := nil;
               bp := pointer(Calloc(sizeof(block)));
               bp^.last := blast;
               blast^.next := bp;
               blast := bp;
               bp^.code := cp;
               cb := cp;
               cp := cp^.next;
               cb^.next := nil;
               end {else if}
            else begin			{all other statements get added to a block}
               cb^.next := cp;
               cb := cp;
               cp := cp^.next;
               cb^.next := nil;
               end; {else}
         end; {if}
      end; {BasicBlocks}


   begin {Generate}             
   					{peephole optimization}
   if peepHole and not fenvAccessInFunction then
      repeat
         rescan := false;
	 PeepHoleOptimization(DAGhead);
	 op := DAGHead;
	 while op^.next <> nil do begin
	    Spin;
            PeepHoleOptimization(op^.next);
            op := op^.next;
            end; {while}
	 CheckLabels;
      until not rescan;
   BasicBlocks;				{build the basic blocks}
   if commonSubexpression or loopOptimizations then
      if not volatile then
         if not fenvAccessInFunction then
            FlagIndirectUses;		{create a list of all indirect uses}
   if commonSubexpression then		{common sub-expression removal}
      if not volatile then
         if not fenvAccessInFunction then
            CommonSubexpressionElimination;
   if loopOptimizations then		{loop optimizations}
      if not volatile then
         if not fenvAccessInFunction then
            DoLoopOptimization;
{  if printSymbols then			{debug}
{     PrintBlocks(@'DAG: ', DAGblocks);	{debug}
   if commonSubexpression or loopOptimizations then
      if not volatile then
         if not fenvAccessInFunction then
            DisposeOpList(c_ind);	{dispose of indirect use list}
   Gen(DAGblocks);			{generate native code}
   if loopOptimizations then		{dump and dynamic space}
      if not volatile then
         if not fenvAccessInFunction then
            DumpLoopLists;
   DAGhead := nil;			{reset the DAG pointers}
   end; {Generate}
   
   
   procedure CheckReturn;
   
   { Check if a noreturn function looks like it might return,   }
   { or if a non-void function might return with no value.      }
   {                                                            }
   { This uses a heuristic of basically looking for code at the }
   { end of the function that would lead to it returning if     }
   { executed.  Control flow operations are optimistically      }
   { assumed not to lead to a return.  This may produce both    }
   { false positives and false negative, but any false          }
   { positives should reflect extraneous code that is not       }
   { actually reachable (which is dubious in its own right).    }
   
   var
      code: icptr;

   begin {CheckReturn}
   code := DAGhead;
   while code^.opcode in [pc_lnm,dc_lab,dc_loc,pc_add] do
      code := code^.next;
   while code^.opcode = pc_pop do
      code := code^.left;
   while code^.opcode = pc_bno do
      code := code^.right;
   if not (code^.opcode in [pc_fjp,pc_tjp,pc_ujp,pc_xjp,pc_cui,pc_cup,pc_tl1])
      then begin
      if fIsNoreturn then
         Error(154)
      else
         Error(155);
      end;
   end; {CheckReturn}


   procedure Push (code: icptr);

   { place a node on the operation stack			}
   {								}
   { parameters:						}
   {    code - node						}

   begin {Push}
   code^.next := DAGhead;
   DAGhead := code;
   end; {Push}


   function Pop: icptr;

   { pop a node from the operation stack			}
   {								}
   { returns: node pointer or nil				}

   var
      node: icptr;			{node poped}
      tn: icptr;			{temp node}

   begin {Pop}
   node := DAGhead;
   if node = nil then
      Error(cge1)
   else begin
      DAGhead := node^.next;
      node^.next := nil;
      end; {else}
   if node^.opcode = dc_loc then begin
      tn := node;
      node := Pop;
      Push(tn);
      end; {if}
   Pop := node;
   end; {Pop}


   procedure Reverse;

   { Reverse the operation stack				}

   var
      list, temp: icptr;		{work pointers}

   begin {Reverse}
   list := nil;
   while DAGhead <> nil do begin
      temp := DAGhead;
      DAGhead := temp^.next;
      temp^.next := list;
      list := temp;
      end; {while}
   DAGhead := list;
   end; {Reverse}


begin {DAG}
case code^.opcode of

   pc_bnt, pc_bnl, pc_cnv, pc_dec, pc_inc, pc_ind, pc_lbf, pc_lbu,
   pc_ngi, pc_ngl, pc_ngr, pc_not, pc_stk, pc_cop, pc_cpo, pc_tl1,
   pc_sro, pc_str, pc_fjp, pc_tjp, pc_xjp, pc_cup, pc_pop, pc_iil,
   pc_ili, pc_idl, pc_ild, pc_bnq, pc_ngq, pc_rbo, pc_rev, pc_ckp:
      begin
      code^.left := Pop;
      Push(code);
      end;

   pc_adi, pc_adl, pc_adr, pc_and, pc_lnd, pc_bnd, pc_bal, pc_bno,
   pc_bor, pc_blr, pc_bxr, pc_blx, pc_cbf, pc_cpi, pc_dvi, pc_mov,
   pc_udi, pc_dvl, pc_udl, pc_dvr, pc_equ, pc_geq, pc_grt, pc_leq,
   pc_les, pc_neq, pc_ior, pc_lor, pc_ixa, pc_mod, pc_uim, pc_mdl,
   pc_ulm, pc_mpi, pc_umi, pc_mpl, pc_uml, pc_mpr, pc_psh, pc_sbi,
   pc_sbl, pc_sbr, pc_shl, pc_sll, pc_shr, pc_usr, pc_slr, pc_vsr,
   pc_tri, pc_sbf, pc_sto, pc_cui, pc_bqr, pc_bqx, pc_baq, pc_adq,
   pc_sbq, pc_mpq, pc_umq, pc_dvq, pc_udq, pc_mdq, pc_uqm, pc_slq,
   pc_sqr, pc_wsr:
      begin
      code^.right := Pop;
      code^.left := Pop;
      Push(code);
      end;

   pc_gil, pc_gli, pc_gdl, pc_gld, pc_lil, pc_lli, pc_ldl, pc_lld,
   pc_lad, pc_lao, pc_lca, pc_lda, pc_ldc, pc_ldo, pc_lod, pc_nop,
   dc_cns, dc_glb, dc_dst, pc_lnm, pc_nam, pc_nat, dc_lab, pc_add,
   pc_ujp, dc_pin, pc_ent, dc_sym, pc_fix:
      Push(code);

   pc_ret:
      begin
      if (lint & lintReturn) <> 0 then
         if fIsNoreturn or ((code^.optype <> cgVoid) and not doingMain) then
            CheckReturn;
      Push(code);
      end;

   pc_ckn:
      begin
      code^.opcode := pc_ckp;
      temp := Pop;
      code^.left := Pop;
      Push(code);
      Push(temp);
      end;

   pc_cnn:
      begin
      code^.opcode := pc_cnv;
      temp := Pop;
      code^.left := Pop;
      Push(code);
      Push(temp);
      end;

   dc_loc: begin
      Push(code);
      if code^.r > maxLoc then
         maxLoc := code^.r;
      end;

   dc_prm: begin
      Push(code);
      if code^.s > maxLoc then
         maxLoc := code^.s;
      end;

   dc_str: begin
      Push(code);
      maxLoc := 0;
      end;

   dc_enp: begin
      Push(code);
      Reverse;
      Generate;
      end;

   otherwise: Error(cge1);		{invalid opcode}
   end; {case}
end; {DAG}

end.
