-- (C) Copyright International Business Machines Corporation 23 January 
-- 1990.  All Rights Reserved. 
--  
-- See the file USERAGREEMENT distributed with this software for full 
-- terms and conditions of use. 
-- File: cgforinsp.pp
-- Author: Andy Lowry
-- SCCS Info: @(#)cgforinsp.pp	1.12 3/13/90

-- This process generates code for FOR...INSPECT statement.  The code
-- is based on an 'allOrNone' loop skeleton produced by cgSelector.
-- Prior to the loop, the source table is copied, and the loop is actually
-- performed on the copy rather than the source table.  This allows
-- the loop body to modify the source table without affecting the
-- sequence of elements inspected by the loop.

-- It is useful to compare the situation here with that of the extract
-- statement, where things are done slightly differently:

--   * In FOR...INSPECT the copy is needed even when the selector does
--     not reference the source table, since the loop body could
--     insert new table elements or remove elements other than the one
--     being examined on the current iteration.  With EXTRACT this is
--     not the case, so the copy can be omitted when the selector does
--     not reference the source table.  In the current case, the copy
--     could be avoided if the loop body does not reference the source
--     table, but in this case the improvement will be quite small,
--     whereas for EXTRACT, omitting the copy operation will almost
--     always save the cost of a deep copy of the table.

--   * In FOR...INSPECT the selector element variable is not needed in
--     any instruction in the loop body where it must be coordinated
--     with the loop itself; rather, it acts as an independent object
--     in all its appearances.  In the EXTRACT statement, the 'remove'
--     instruction in the loop body must have as its source operands
--     an element variable and a table that are currently correlated
--     via a lookup loop.  Thus in the EXTRACT statement the loop must
--     be performed directly on the source table, whereas in
--     FOR...INSPECT the copy serves just as well.

-- A 'scan' style loop ends up looking like this:
--
--	copy		t0, t
-- 	initget 	r,t0
-- loop:get_or_goto 	r,t0 {endloop}
--	[additional tests, result in 'test']
--	bfalse 		test {loop}
--	[loop body]
--	branch		{loop}
-- endloop:
--	endget		r,t
--      discard		t0
--

#include "typemark.h"
#include "codegen.h"

cgForInsp: using (cgInternal, interpform)

process (Q: cgStmtQ)

declare
  args: cgStmt;
  tbl: objectname;		-- source table
  tblAddr: interpform!operand;	-- its LI address
  loopTAddr: interpform!operand;-- LI addr of table to loop over
  lk: lookupInfo;		-- describes lookup method used in loop
  endLoop: cgSelectorContinueFn; -- call this to tie off the loop
  inspElt: objectname;		-- element variable in inspect body scope
  empty: empty;

begin
  receive args from Q;
  reveal args.stmt.qualifier.inspect_table;

  -- Get source table LI address
  tbl <- objectname#(AREF(tmp,args.stmt.operands,ZERO));
  tblAddr <- interpform!operand#(args.cgData.Proc.objAddr(tbl));

  -- Get an address for the table copy, and generate the 'copy'
  loopTAddr <- interpform!operand#(args.cgData.Proc.tmpAddr());
  block declare
    op: interpform!operation;
  begin
    new op;
    op.opcode <- interpform!opcode#'copy';
    new op.operands;
    insert interpform!operand#(copy of loopTAddr) into op.operands;
    insert interpform!operand#(copy of tblAddr) into op.operands;
    unite op.qualifier.empty from empty;
    ADDINSTR(op);
  end block;

  -- Now generate the top part of the loop skeleton
  endLoop <- cgSelectorContinueFn#(FNS.cgSelector(
      args.stmt.qualifier.inspect_table.selector, args.cgData,
      typename#(args.cgData.Proc.objType(tbl)),
      selectorLoopType#'allOrNone', lk, loopTAddr));

  -- We force the selector element variable and the inpsect element
  -- variable to be aliased here to avoid a copy operation... in the
  -- current back-end this can never cause a problem. (famous last
  -- words!)  We also need to add a selectorInfo entry to the scratch
  -- pad for the inspect variable, with all the same info as for the
  -- selector element variable
  block declare
    selEltAddr: interpform!operand;
    selRoot: predefined!rootname;
    si: selectorInfoEntry;
  begin
    selEltAddr <- interpform!operand#(args.cgData.Proc.rootAddr(
	args.stmt.qualifier.inspect_table.selector.element,
	args.stmt.qualifier.inspect_table.selector.scope));
    new inspElt;		-- need to build objectname from rootid
    new inspElt.root;
    inspElt.root.root := args.stmt.qualifier.inspect_table.element;
    inspElt.root.scope := args.stmt.qualifier.inspect_table.scope;
    new inspElt.components;
    call args.cgData.Proc.setAddr(inspElt,selEltAddr);
    new selRoot;
    selRoot.root := args.stmt.qualifier.inspect_table.selector.element;
    selRoot.scope := args.stmt.qualifier.inspect_table.selector.scope;
    si := selectorInfoEntry#(args.cgData.scratch.selInfo[selRoot]);
    si.elt := inspElt.root;
    insert si into args.cgData.scratch.selInfo;
  end block;

  -- Now generate the loop body, supplied in the FOR..INSPECT
  -- statement qualifier
  inspect proc in ABSPROG.programs[args.cgData.Proc.id] begin
    inspect scope in proc.executable_part.scopes
	[args.stmt.qualifier.inspect_table.scope] begin
      call FNS.cgClause(scope.clause, args.cgData);
    end inspect;
  end inspect;

  -- Undo the alias we set earlier
  block declare
    null: interpform!operand;
  begin
    new null;
    call args.cgData.Proc.setaddr(inspElt,null);
  end block;
  
  -- remove the selectorInfo entry we made for the inspect element
  block declare
    si: selectorInfoEntry;
  begin
    remove si from args.cgData.scratch.selInfo[inspElt.root];
  end block;
  

  -- Now finish up the loop skeleton
  call endLoop(args.cgData);

  -- Discard the table copy we made
  block declare
    op: interpform!operation;
  begin
    op <- interpform!operation#(copy of args.cgData.Tplt.discard);
    insert loopTAddr into op.operands;
    ADDINSTR(op);
  end block;

  return args;

end process
