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

-- This function generates LI code for a single Hermes absprog
-- process.  The process is first transformed into a collection of
-- basic blocks containing LI instructions, with all branch targets
-- set to basic block ID's.  Next the basic blocks are submitted to
-- the optimizer, which returns a (hopefully) improved set of basic
-- blocks.  Finally, the basic blocks are strung together into a
-- single ordered table of LI code instructions, with branch targets
-- resolved to indices into the table.

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

cgProcess: using (interpform, cgInternal, common, parse)

process (Q: cgProcessQ)

declare
  args: cgProcess;
  execEnv: execution_environment;
  shutdown: signalPort;
  empty: empty;
begin
  receive args from Q;
    
  -- initialize per-process auxiliary data
  call FNS.cgProcInit(args.processid,args.cgData,shutdown);

  -- allocate the result object
  new args.LIprog;
  args.LIprog.id := args.processid;

  -- locate the main clause of the process
  inspect proc in ABSPROG.programs[args.processid] begin
    execEnv := proc.executable_part;
    inspect scope in execEnv.scopes[execEnv.main_scope] begin

      -- codegen the main clause and, recursively, all the other
      -- clauses in the process
      call FNS.cgClause(scope.clause,args.cgData);

      -- fill in the initport typename while we've got the proc and
      -- scope isolated
      inspect decl in scope.declarations[proc.initport] begin
	reveal decl.typename.typename; -- initport must be named
	args.LIprog.type := decl.typename.typename;
      end inspect;

    end inspect;
  end inspect;

  -- Tie off the code with an 'endprocess' instruction in the final
  -- basic block, then install the final BB in BBData
  ADDTPLT(endprocess);
  unite CURBB.exit.none from empty;
  insert BBid#(copy of CURBB.id) into BBDATA.BBOrder;
  insert CURBB into BBDATA.BBs;

  -- Get the number of registers needed from the address mapper
  BBDATA.nreg <- I(args.cgData.Proc.nRegUsed());

# ifndef LIGEN
    block declare
      arb: polymorph;
      copy: BBData;
    begin
      inspect opt in args.cgData.options[S("bbgen")] begin
	copy := BBDATA;
	wrap copy as arb;
	call args.cgData.std.libWriteObj(S("BB.preopt"),arb);
      end inspect;
    on (NotFound)
    end block;
# endif   

  -- At this point all the code has been generated, but branches are
  -- encoded in the exit data of the basic blocks.  The following
  -- optimization phase may transform the basic blocks in any fashion,
  -- including reordering.  It is allowable for the optimizer to do
  -- absolutely nothing.
# ifndef LIGEN
    call FNS.optimize(BBDATA);
# endif

# ifndef LIGEN
    block declare
      arb: polymorph;
      copy: BBData;
    begin
      inspect opt in args.cgData.options[S("bbgen")] begin
	copy := BBDATA;
	wrap copy as arb;
	call args.cgData.std.libWriteObj(S("BB.postopt"),arb);
      end inspect;
    on (NotFound)
    end block;
# endif

  -- Process data vector size is computed from the size of the
  -- register set used by the basic blocks
  args.LIprog.size := BBDATA.nreg;

  -- Now we just string the basic blocks together in the current
  -- ordering, inserting branches as necessary to satisfy the exit
  -- specifications.
  args.LIprog.code <- interpform!code#(FNS.BBAssemble(BBDATA));

  -- See if there's a "Process Names" annotation, and if so, try to
  -- find a name for this process.  Failing that, look for a "Module
  -- Name" annotation and use its value for the main process.  Other
  -- processes get names formed from the module name.  If neither
  -- annotation exists, construct a relatively useless name.
  block declare
    pmaps: executable_printmappings;
    execId: executable_id;
    arb: polymorph;
  begin
    inspect pnamesAnnotation in args.cgData.Prog.annotations
	  [S("Process Names")]
    begin
      arb := pnamesAnnotation.thing;
      unwrap pmaps from arb { init };
      unite execId.pid from processid#(copy of args.processid);
      inspect pmap in pmaps[execId] begin
	args.LIprog.name := pmap.name;
      end inspect;
    end inspect;
  on (NotFound)			-- for either inspect
    block declare
      modname: charstring;
    begin
      inspect modnameAnnotation in args.cgData.Prog.annotations
	    [S("Module Name")] begin
	arb := modnameAnnotation.thing;
	unwrap modname from arb {init};
	if B(args.processid = ABSPROG.main_program) then
	  args.LIprog.name <- modname;
	else
	  args.LIprog.name <- S(modname | S("_proglit"));
	end if;
      end inspect;
    on (NotFound)
      args.LIprog.name <- S("unnamed_process");
    end block;
  end block;
	
  -- Build the link table for statically linked external programs
  block declare
    links: parse!linkedPrograms;
  begin
    -- Get the link table built by the parser from its annotation
    block declare
      arb: polymorph;
    begin
      inspect linksAnnotation in args.cgData.Prog.annotations
	    [S("Linked Programs")] begin
	arb := linksAnnotation.thing;
	unwrap links from arb {init};
      end inspect;
    on (NotFound)
      new links;		-- no such annotation... no links
    end block;
    -- Get the program object for each linked process id, and build up
    -- the link table for the LI prog
    new args.LIprog.linkedProgs;
    for pid in args.cgData.Proc.linkedPids[] inspect
      inspect linkedProg in links where (B(linkedProg.id = pid)) begin
	insert program#(copy of linkedProg.program)
	    into args.LIprog.linkedProgs;
      end inspect;
    end for;
  end block;
  
  return args;			-- all done... send back the results

  -- shut down any servers we started on behalf of this process
  send empty to shutdown;
  
end process
