-- (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: cgselect.pp
-- Author: Andy Lowry
-- SCCS Info: @(#)cgselect.pp	1.8 3/15/90

-- This module translates SELECT statements.  The various boolean
-- guard computations are generated first, then a basic block exit
-- structure reflecting the various select clauses is used to tie off
-- the current basic block.  All of the select clauses is then
-- generated in turn, each terminated with a jump-style exit to a
-- merge-point basic block.  The merge point is left as the current
-- BB.

-- In the case of a select statement with an operand to the select
-- itself (so the WHERE guards are meant to be compared to the SELECT
-- operand), the WHERE guard values are computed by the clauses stored
-- under the 'boolean' guard info variant.  In this case, new
-- temporary variables are created to hold the results of the
-- comparisons, and 'equal' instructions are inserted to compute their
-- values.  They are all discarded at the insert point.

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

cgSelect: using (cginternal, interpform)

process (Q: cgStmtQ)
  
declare
  args: cgStmt;
  null: interpform!operand;
  clauseBBids: BBidList;
  otherBBid: BBid;
  mergeBBid: BBid;
  valAddr: interpform!operand;
  tmps: interpform!operand_list;
  tmpsCopy: interpform!operand_list;
  empty: empty;
begin
  receive args from Q;
  reveal args.stmt.qualifier.select;

  new null;			-- empty operand for missing guard parts

  -- Get address of SELECT operand if there is one, else set it to a
  -- null operand
  if B(I(size of args.stmt.operands) = ZERO) then
    valAddr := null;
  else
    valAddr <- interpform!operand#(args.cgData.Proc.objAddr(
	objectname#(AREF(tmp,args.stmt.operands,ZERO))));
  end if;
  -- Allocate list of tmp addresses in case it's needed
  new tmps;

  -- translate all the clauses that compute boolean guard values (or
  -- values to be compared to a SELECT operand)
  for c in args.stmt.qualifier.select.clauses[] inspect
    select predefined!guard_type#(case of c.info)
    where (predefined!guard_type#'boolean')
      -- boolean-only guard
      reveal c.info.boolean;
      call FNS.cgClause(c.info.boolean.clause,args.cgData);
      -- If there was a SELECT operand, generate a comparison to the
      -- result of the above clause, and store the comparison result
      -- in a tmp address
      if B(valAddr <> null) then
	block declare
	  tmp: interpform!operand;
	  op: interpform!operation;
	begin
	  tmp <- interpform!operand#(args.cgData.Proc.tmpAddr());
	  insert interpform!operand#(copy of tmp) into tmps;
	  new op;
	  op.opcode <- interpform!opcode#'equal';
	  new op.operands;
	  insert tmp into op.operands;
	  insert interpform!operand#(copy of valAddr) into op.operands;
	  insert interpform!operand#(
	    args.cgData.Proc.objAddr(c.info.boolean.result)) into op.operands;
	  unite op.qualifier.empty from empty;
	  ADDINSTR(op);
	end block;
      end if;
      
    where (predefined!guard_type#'both')
      -- boolean and event guard
      reveal c.info.both;
      call FNS.cgClause(c.info.both.boolean.clause,args.cgData);
      
    where (predefined!guard_type#'event')
      -- event-only guard... no boolean computation to codegen
      
    otherwise
      exit cantHappen;

    end select;
  end for;
  
  -- Make a copy of the list of temp addresses
  tmpsCopy := tmps;

  -- Now allocate BBid's for the various clauses, the 'otherwise'
  -- clause, and the merge point
  new clauseBBids;
  for c in args.stmt.qualifier.select.clauses[] inspect
    insert BBid#unique into clauseBBids;
  end for;
  otherBBid <- BBid#unique;
  mergeBBid <- BBid#unique;
  
  -- Build up the exit structure for the current BB
  block declare
    se: BBSelectExit;
  begin
    new se;
    se.targets := clauseBBids;
    se.other := otherBBid;
    unite CURBB.exit.selection from se;
  end block;
  
  -- Now build up the select instruction as the final instruction in
  -- the BB.
  block declare
    op: interpform!operation;
    addr: interpform!operand;
    sq: interpform!select_qual;
  begin
    new op;
    op.opcode <- interpform!opcode#'select';
    new op.operands;
    for c in args.stmt.qualifier.select.clauses[] inspect
      select predefined!guard_type#(case of c.info)
      where (predefined!guard_type#'boolean')
	-- boolean-only guard... use null for event part.  Boolean
	-- part is either the boolean guard result, or the tmp we
	-- allocated for a 'SELECT expr' style statement
	insert interpform!operand#(copy of null) into op.operands;
	if B(valAddr = null) then
	  reveal c.info.boolean;
	  insert interpform!operand#(args.cgData.Proc.objAddr
		(c.info.boolean.result)) into op.operands;
	else
	  remove addr from AREF(tmp,tmps,ZERO);
	  insert addr into op.operands;
	end if;
	
      where (predefined!guard_type#'event')
	-- event-only guard... use null for boolean part
	reveal c.info.portname;
	insert interpform!operand#(args.cgData.Proc.objAddr
	      (c.info.portname)) into op.operands;
	insert interpform!operand#(copy of null) into op.operands;
	
	
      where (predefined!guard_type#'both')
	-- combined boolean/event guard... both parts get real addresses
	reveal c.info.both;
	insert interpform!operand#(args.cgData.Proc.objAddr
	      (c.info.both.portname)) into op.operands;
	insert interpform!operand#(args.cgData.Proc.objAddr
	      (c.info.both.boolean.result)) into op.operands;

      otherwise
	exit cantHappen;
      end select;
      
    end for;
    
    new sq;
    unite op.qualifier.select from sq;
    ADDINSTR(op);
  end block;
  
  -- Now translate the body of each clause in turn
  for c in args.stmt.qualifier.select.clauses[] inspect
    block declare
      clauseBBid: BBid;
    begin
      -- Get next BBid from our list and make it current
      remove clauseBBid from AREF(tmp,clauseBBids,ZERO);
      NEWBB(clauseBBid);
      -- translate the clause
      call FNS.cgClause(c.clause,args.cgData);
      -- Tie it off with a jump to the merge point
      unite CURBB.exit.jump from BBid#(copy of mergeBBid);
    end block;
  end for;
  
  -- Now codegen the 'otherwise' clause and tie it off with a jump to
  -- the merge point
  NEWBB(otherBBid);
  call FNS.cgClause(args.stmt.qualifier.select.otherwise_clause,args.cgData);
  unite CURBB.exit.jump from BBid#(copy of mergeBBid);

  -- Now open up the merge point as the current BB
  NEWBB(mergeBBid);
  
  -- All temporaries are scalars, so we need not discard them
#ifdef undefined
  -- Generate discards for any temps we created
  block declare
    op: interpform!operation;
    tmp: interpform!operand;
  begin
    while B(I(size of tmpsCopy) <> ZERO) repeat
      remove tmp from tmpsCopy[];
      op := args.cgData.Tplt.discard;
      insert tmp into op.operands;
      ADDINSTR(op);
    end while;
  end block;
#endif

  return args;
  
on exit(cantHappen)
  print S("CantHappen exit taken in cgselect");
end process
