-- (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: cgforenum.pp
-- Author: Andy Lowry
-- SCCS Info: @(#)cgforenum.pp	1.5 3/13/90

-- This module translates a FOR...ENUMERATE statement.  A typical
-- translation looks as follows:
--
--	oeinitloop	x
--loop:	[body]
--	oeloop		x {done,size}
--done:
--

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

cgForEnum: using (cgInternal, interpform)

process (Q: cgStmtQ)
  
declare
  args: cgStmt;
  op: interpform!operation;
  iter: predefined!objectname;
  iterAddr: interpform!operand;
  iterTDef: predefined!type_definition;
  loopBBid: BBid;
  doneBBid: BBid;
  empty: empty;
begin
  receive args from Q;
  reveal args.stmt.qualifier.for_enumerate;
  
  -- build up a full objectname for the iterator
  new iter;
  new iter.root;
  iter.root.root := args.stmt.qualifier.for_enumerate.enumerator;
  iter.root.scope := args.stmt.qualifier.for_enumerate.scope;
  new iter.components;
  -- get its LI address and type
  iterAddr <- interpform!operand#(args.cgData.Proc.objAddr(iter));
  iterTDef <- type_definition#(FNS.typeDef(
      typename#(args.cgData.Proc.objType(iter))));
  reveal iterTDef.specification.enumeration;
  
  -- Don't do anything if the enumeration type is empty
  if B(I(size of iterTDef.specification.enumeration.values) = ZERO) then
    return args;
    exit done;
  end if;

  -- Allocate basic block ID's for loop top, body, and exit
  loopBBid <- BBid#unique;
  doneBBid <- BBid#unique;

  -- Build the initloop instruction
  new op;
  op.opcode <- interpform!opcode#'oeinitloop';
  new op.operands;
  insert interpform!operand#(copy of iterAddr) into op.operands;
  unite op.qualifier.empty from empty;
  ADDINSTR(op);
  
  -- Tie off the current BB with a jump to the loop top, and open the
  -- loop basic block
  unite CURBB.exit.jump from BBid#(copy of loopBBid);
  NEWBB(copy of loopBBid);

  -- Now codegen the loop body
  inspect prog in ABSPROG.programs[args.cgData.Proc.id] begin
    inspect scope in prog.executable_part.scopes
	  [args.stmt.qualifier.for_enumerate.scope] begin
      call FNS.cgClause(scope.clause,args.cgData);
    end inspect;
  end inspect;
  
  -- Now build the loop test instruction and put it in a 'test' style
  -- BB exit structure
  new op;
  op.opcode <- interpform!opcode#'oeloop';
  new op.operands;
  insert iterAddr into op.operands;
  block declare
    ip: interpform!integer_pair;
  begin
    new ip;
    ip.int_one <- ZERO;		-- filled in with actual label during
				-- basic block assembly
    ip.int_two <- I(size of iterTDef.specification.enumeration.values);
    unite op.qualifier.integer_pair from ip;
  end block;
  block declare
    te: BBTestExit;
  begin
    new te;
    te.nojump := doneBBid;
    te.jump <- loopBBid;
    unite CURBB.exit.test from te;
  end block;
  -- Add the test instruction as the final BB instruction to accompany
  -- the above exit structure
  ADDINSTR(op);

  -- Finally open up the loop exit BBid
  NEWBB(doneBBId);
  
  return args;
  
on exit(done)
  -- exit early here for empty enumeration
end process
