-- file Pass4L.Mesa
-- last modified by Satterthwaite, December 6, 1979  1:09 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [charlength, maxword, wordlength],
  ComData: FROM "comdata"
    USING [
      definitionsOnly, idANY, importCtx, linkBase, linkCount, mainBody,
      nBodies, nSigCodes, stopping, switches, textIndex],
  CompilerUtil: FROM "compilerutil" USING [AppendBCDWord],
  ControlDefs: FROM "controldefs"
    USING [EPRange, globalbase, localbase, MaxFrameSize, MaxNGfi],
  Log: FROM "log" USING [Error, ErrorN, ErrorSei, WarningSei],
  P4: FROM "p4",
  Symbols: FROM "symbols"
    USING [bodyType, ctxType, seType,
      BitAddress, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex,
      BTIndex, CBTIndex,
      HTNull, SENull, ISENull, CTXNull, BTNull, lG, lL],
  SymbolOps: FROM "symbolops"
    USING [
      BitsForRange, Cardinality, LinkMode, MakeCtxSe, NextSe,
      UnderType, XferMode],
  SystemDefs: FROM "systemdefs" USING [AllocateHeapNode, FreeHeapNode],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType, Index, Scan, NullIndex],
  TreeOps: FROM "treeops" USING [ScanList];

Pass4L: PROGRAM
    IMPORTS
	CompilerUtil, Log, SymbolOps, SystemDefs, TreeOps,
	dataPtr: ComData
    EXPORTS P4 =
  BEGIN
  OPEN SymbolOps, Symbols;

  tb: Table.Base;	-- tree base (local copy)
  seb: Table.Base;	-- se table base (local copy)
  ctxb: Table.Base;	-- context table base (local copy)
  bb: Table.Base;	-- body table base (local copy)

  LayoutNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];
    bb ← base[bodyType];
    END;


 -- address assignment (machine sensitive and subject to change)

  WordLength: CARDINAL = AltoDefs.wordlength;
  WordFill: CARDINAL = WordLength-1;
  ByteLength: CARDINAL = AltoDefs.charlength;
  BytesPerWord: CARDINAL = WordLength/ByteLength;

  LocalOrigin: CARDINAL = ControlDefs.localbase*WordLength;
  LocalSlots: CARDINAL = 8;
  GlobalOrigin: CARDINAL = ControlDefs.globalbase*WordLength;
  FrameLimit: CARDINAL = ControlDefs.MaxFrameSize*WordLength;

  EntryLimit: CARDINAL = ControlDefs.MaxNGfi * ControlDefs.EPRange;


  BitsForType: PUBLIC PROCEDURE [type: SEIndex] RETURNS [nBits: CARDINAL] =
    BEGIN  -- assumes (an attempt at) prior processing by P4declitem
    b, n, nW: CARDINAL;
    sei: CSEIndex ← UnderType[type];
    WITH seb[sei] SELECT FROM
      basic =>  nBits ← length;
      enumerated =>  nBits ← BitsForRange[Cardinality[sei]-1];
      pointer =>  nBits ← WordLength;
      transfer =>  nBits ← IF mode = port THEN 2*WordLength ELSE WordLength;
      arraydesc =>  nBits ← 2*WordLength;
      relative =>  nBits ← BitsForType[offsetType];
      long =>
	BEGIN
	nW ← (BitsForType[rangeType] + WordFill)/WordLength;
	nBits ← (nW + 1)*WordLength;
	END;
      real =>  nBits ← 2*WordLength;
      ENDCASE =>  -- processing of se entry must be complete
	BEGIN
	IF ~mark4
	  THEN
	    BEGIN  -- P4declitem has not been able to complete
	    Log.ErrorSei[typeLength,
		IF seb[type].seTag = id
		  THEN LOOPHOLE[type, ISEIndex]
		  ELSE ISENull];
	    RETURN [0]
	    END;
	WITH seb[sei] SELECT FROM
	  record =>  BEGIN  nBits ← length;  lengthUsed ← TRUE  END;
	  array =>
	    BEGIN
	    b ← BitsForType[componentType];  n ← Cardinality[indexType];
	    IF oldPacked AND b <= ByteLength
	      THEN
		nW ← n/BytesPerWord+(IF n MOD BytesPerWord = 0 THEN 0 ELSE 1)
	      ELSE
		BEGIN
		b ← ((b + WordFill)/WordLength)*WordLength;
		IF n > AltoDefs.maxword/b THEN Log.Error[fieldSize];
		nW ← n * (b/WordLength);
		END;
	    nBits ← nW*WordLength;  lengthUsed ← TRUE;
	    END;
	  subrange =>
	    nBits ← IF empty THEN 0 ELSE BitsForRange[Cardinality[sei]-1];
	  ENDCASE =>  nBits ← 0;
	END;
    RETURN
    END;


 -- profile utilities

  VarLink: TYPE = RECORD [
    SELECT kind: * FROM
      symbol => [index: ISEIndex],
      body => [index: CBTIndex],
      empty => NULL,
      ENDCASE];

  VarInfo: TYPE = RECORD [link: VarLink, nRefs: CARDINAL];
  Profile: TYPE = DESCRIPTOR FOR ARRAY OF VarInfo;

  AllocateProfile: PROCEDURE [n: INTEGER] RETURNS [profile: Profile] =
    BEGIN
    k: INTEGER;
    profile ← DESCRIPTOR [SystemDefs.AllocateHeapNode[n*SIZE[VarInfo]], n];
    FOR k IN [0 .. n) DO  profile[k].link ← [empty[]]  ENDLOOP;
    RETURN
    END;

  ReleaseProfile: PROCEDURE [profile: Profile] =
    BEGIN  SystemDefs.FreeHeapNode[BASE[profile]]  END;

  SortProfile: PROCEDURE [v: Profile] =
    BEGIN  -- Shell sort --
    h, i, j: INTEGER;
    k: CARDINAL;
    t: VarInfo;
    h ← LENGTH [v];
      DO
      h ← h/2;
      FOR j IN [h .. LENGTH[v])
	DO
	i ← j-h;  k ← v[j].nRefs;  t ← v[j];
	WHILE k > v[i].nRefs
	  DO
	  v[i+h] ← v[i];
	  IF (i ← i-h) < 0 THEN EXIT;
	  ENDLOOP;
	v[i+h] ← t;
	ENDLOOP;
      IF h <= 1 THEN EXIT;
      ENDLOOP;
    END;


 -- entry point assignment

  GenBodies: PROCEDURE [root: BTIndex, proc: PROCEDURE [CBTIndex]] =
    BEGIN
    bti, next: BTIndex;
    FOR bti ← root, next UNTIL bti = BTNull
      DO
      WITH bb[bti] SELECT FROM
	Callable => proc[LOOPHOLE[bti]];
	ENDCASE => NULL;
      IF bb[bti].firstSon # BTNull
	THEN  next ← bb[bti].firstSon
	ELSE
	  DO
	  next ← bb[bti].link.index;
	  IF next = BTNull OR bb[bti].link.which # parent THEN EXIT;
	  bti ← next;
	  ENDLOOP;
      ENDLOOP;
    END;


  BodyRefs: PROCEDURE [bti: CBTIndex] RETURNS [count: CARDINAL] =
    BEGIN
    sei: ISEIndex;
    node: Tree.Index;

    CountRefs: Tree.Scan =
      BEGIN
      count ← WITH t SELECT FROM
	symbol =>  count + seb[index].idInfo,
	ENDCASE =>  ERROR;
      END;

    count ← 0;
    IF (sei ← bb[bti].id) # SENull
      THEN
	BEGIN
	node ← seb[sei].idValue; TreeOps.ScanList[tb[node].son[1], CountRefs];
	END;
    RETURN
    END;


  AssignEntries: PUBLIC PROCEDURE [rootBti: BTIndex] =
    BEGIN
    i, j, k: INTEGER;
    profile: Profile;
    bti: CBTIndex;

    AssignSlot: PROCEDURE [bti: CBTIndex] =
      BEGIN
      IF ~bb[bti].inline AND bb[bti].info.mark = Internal
	THEN
	  BEGIN
	  n: CARDINAL = BodyRefs[bti];
	  profile[k].link ← [body[index: bti]];
	  WITH body: bb[bti] SELECT FROM
	    Inner =>  BEGIN body.frameOffset ← n; profile[k].nRefs ← 0 END;
	    ENDCASE =>  profile[k].nRefs ← n;
	  k ← k+1;
	  END;
      END;

    nEntries: CARDINAL = MAX[dataPtr.nBodies, dataPtr.nSigCodes];
    IF nEntries > EntryLimit
      THEN Log.ErrorN[bodyEntries, nEntries-EntryLimit];
    profile ← AllocateProfile[dataPtr.nBodies];
    k ← 0;  GenBodies[rootBti, AssignSlot];
    IF dataPtr.switches['s] THEN SortProfile[profile];
    i ← 1;
    FOR j IN [0..LENGTH[profile])
      DO
      bti ← WITH profile[j].link SELECT FROM body => index, ENDCASE => ERROR;
      IF bti = dataPtr.mainBody
	THEN bb[bti].entryIndex ← 0
	ELSE  BEGIN  bb[bti].entryIndex ← i;  i ← i+1  END;
      ENDLOOP;
    ReleaseProfile[profile];
    END;


 -- frame layout

  VarScan: TYPE = PROCEDURE [sei: ISEIndex, output: BOOLEAN];

  GenCtxVars: PROCEDURE [ctx: CTXIndex, p: VarScan, output: BOOLEAN] =
    BEGIN
    sei: ISEIndex;
    IF ctx # CTXNull THEN
      FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
	DO
	IF ~seb[sei].constant THEN p[sei, output];
	ENDLOOP;
    END;

  GenBodyVars: PROCEDURE [bti: CBTIndex, p: VarScan] =
    BEGIN
    type: SEIndex = bb[bti].ioType;
    WITH se: seb[type] SELECT FROM
      cons =>
	WITH se SELECT FROM
	  transfer =>
	    BEGIN
	    IF inRecord # SENull
	      THEN GenCtxVars[seb[inRecord].fieldCtx, p, FALSE];
	    IF outRecord # SENull
	      THEN GenCtxVars[seb[outRecord].fieldCtx, p, TRUE];
	    END;
	  ENDCASE;
      ENDCASE;
    GenCtxVars[bb[bti].localCtx, p, FALSE];
    END;

  GenBodyProcs: PROCEDURE [bti: BTIndex, proc: PROCEDURE [CBTIndex]] =
    BEGIN
    sonBti: BTIndex;
    IF (sonBti ← bb[bti].firstSon) # BTNull
      THEN
	DO
	WITH body: bb[sonBti] SELECT FROM
	    Callable => IF ~body.inline THEN proc[LOOPHOLE[sonBti]];
	    ENDCASE => NULL;
	IF bb[sonBti].link.which = parent THEN EXIT;
	sonBti ← bb[sonBti].link.index;
	ENDLOOP;
    END;

  GenImportedVars: PROCEDURE [p: VarScan] =
    BEGIN
    sei: ISEIndex;
    type: CSEIndex;
    ctx: CTXIndex = dataPtr.importCtx;
    IF ctx # CTXNull THEN
      FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
	DO
	IF ~seb[sei].constant
	  THEN p[sei, FALSE]
	  ELSE
	    BEGIN  type ← UnderType[seb[sei].idType];
	    WITH seb[type] SELECT FROM
	      definition =>  GenCtxVars[defCtx, p, FALSE];
	      ENDCASE;
	    END;
	ENDLOOP;
    END;



  MarkArg: VarScan = BEGIN  seb[sei].mark4 ← TRUE  END;

  MarkArgs: PROCEDURE [sei: SEIndex] =
    BEGIN
    type: CSEIndex = UnderType[sei];
    rSei: RecordSEIndex;
    WITH seb[type] SELECT FROM
      transfer =>
	BEGIN
	IF (rSei ← inRecord) # SENull THEN
	  BEGIN
	  GenCtxVars[seb[rSei].fieldCtx, MarkArg, FALSE];
	  seb[rSei].length ← LayoutArgs[rSei, 0, TRUE]*WordLength;
	  seb[rSei].mark4 ← TRUE;
	  END;
	IF (rSei ← outRecord) # SENull THEN
	  BEGIN
	  GenCtxVars[seb[rSei].fieldCtx, MarkArg, TRUE];
	  seb[rSei].length ← LayoutArgs[rSei, 0, TRUE]*WordLength;
	  seb[rSei].mark4 ← TRUE;
	  END;
	mark4 ← TRUE;
	END;
      ENDCASE;
    END;


  LayoutLocals: PUBLIC PROCEDURE [bti: CBTIndex] RETURNS [length: CARDINAL] =
    BEGIN
    vProfile: Profile;
    vI: CARDINAL;
    
    CountVar: VarScan =
      BEGIN
      IF seb[sei].hash # HTNull OR ~output THEN vI ← vI + 1;
      END;

    CountProc: PROCEDURE [bti: CBTIndex] =
      BEGIN
      IF bb[bti].info.mark = Internal THEN vI ← vI + 1;
      END;

    InsertVar: VarScan =
      BEGIN
      saveIndex: CARDINAL = dataPtr.textIndex;
      node: Tree.Index = LOOPHOLE[seb[sei].idValue];
      nW: CARDINAL;
      IF node # Tree.NullIndex THEN dataPtr.textIndex ← tb[node].info;
      IF seb[sei].hash # HTNull OR ~output
	THEN
	  BEGIN
	  vProfile[vI] ← [link: [symbol[sei]], nRefs: seb[sei].idInfo];
	  vI ← vI+1;
	  END;
      IF seb[sei].idInfo = 0 AND seb[sei].hash # HTNull
       AND ~output	-- suppress message for return record
       AND node # Tree.NullIndex
	THEN Log.WarningSei[unusedId, sei];
      nW ← (BitsForType[seb[sei].idType] + WordFill)/WordLength;
      seb[sei].idInfo ← nW*WordLength;  seb[sei].idValue ← 0;
      dataPtr.textIndex ← saveIndex;
      END;

    InsertProc: PROCEDURE [bti: CBTIndex] =
      BEGIN
      IF bb[bti].info.mark = Internal
	THEN
	  BEGIN
	  vProfile[vI] ← VarInfo[
	    link: [body[bti]],
	    nRefs: WITH bb[bti] SELECT FROM Inner=>frameOffset, ENDCASE=>0];
	  vI ← vI+1;
	  END;
      END;

    origin: CARDINAL;
    bodyType: SEIndex = bb[bti].ioType;
    IF ~seb[bodyType].mark4 THEN MarkArgs[bodyType];
    vI ← 0;  GenBodyVars[bti, CountVar];  GenBodyProcs[bti, CountProc];
    vProfile ← AllocateProfile[vI];
    vI ← 0;  GenBodyVars[bti, InsertVar];  GenBodyProcs[bti, InsertProc];
    SortProfile[vProfile];
    origin ← IF bb[bti].level = lL
	THEN LocalOrigin
	ELSE LocalOrigin + WordLength;
    origin ← AssignVars[vProfile, origin, LocalOrigin + LocalSlots*WordLength];
    length ← AssignVars[vProfile, origin, FrameLimit];
    CheckFrameOverflow[vProfile];  ReleaseProfile[vProfile];
    RETURN
    END;


  LayoutGlobals: PUBLIC PROCEDURE [bti: CBTIndex] RETURNS [length: CARDINAL] =
    BEGIN
    vProfile, xProfile: Profile;
    vI, xI: CARDINAL;

    CountVar: VarScan =
      BEGIN
      ctx: CTXIndex = seb[sei].idCtx;
      IF ctxb[ctx].ctxType = imported OR ctx = dataPtr.importCtx
	THEN  xI ← xI + 1
	ELSE  IF seb[sei].hash # HTNull OR ~output THEN  vI ← vI + 1;
      END;

    InsertVar: VarScan =
      BEGIN
      saveIndex: CARDINAL;
      ctx: CTXIndex = seb[sei].idCtx;
      node: Tree.Index;
      nW: CARDINAL;
      IF ctxb[ctx].ctxType = imported OR ctx = dataPtr.importCtx
	THEN
	  BEGIN
	  xProfile[xI] ← [link: [symbol[sei]], nRefs: seb[sei].idInfo];
	  xI ← xI+1;
	  IF seb[sei].idInfo = 0 AND ~seb[sei].public
	    THEN Log.WarningSei[unusedId, sei];
	  seb[sei].idInfo ←
	    ((BitsForType[seb[sei].idType]+WordFill)/WordLength)*WordLength;
	  END
	ELSE
	  BEGIN  saveIndex ← dataPtr.textIndex;
	  node ← LOOPHOLE[seb[sei].idValue];
	  IF node # Tree.NullIndex
	    THEN dataPtr.textIndex ← tb[node].info;
	  IF seb[sei].hash # HTNull OR ~output
	    THEN
	      BEGIN
	      vProfile[vI] ← [link: [symbol[sei]], nRefs: seb[sei].idInfo];
	      vI ← vI + 1;
	      END;
	  IF seb[sei].idInfo = 0 AND ~dataPtr.definitionsOnly
	   AND ~seb[sei].public AND seb[sei].hash # HTNull
	   AND node # Tree.NullIndex
	    THEN Log.WarningSei[unusedId, sei];
	  nW ← (BitsForType[seb[sei].idType] + WordFill)/WordLength;
	  seb[sei].idInfo ← nW*WordLength;  seb[sei].idValue ← 0;
	  dataPtr.textIndex ← saveIndex;
	  END;
      END;

    origin: CARDINAL;
    IF ~seb[bb[bti].ioType].mark4 THEN ERROR;
    vI ← xI ← 0;  GenBodyVars[bti, CountVar];  GenImportedVars[CountVar];
    vProfile ← AllocateProfile[vI];  xProfile ← AllocateProfile[xI];
    vI ← xI ← 0;  GenBodyVars[bti, InsertVar];  GenImportedVars[InsertVar];
    IF dataPtr.switches['s]
      THEN  BEGIN  SortProfile[vProfile];  SortProfile[xProfile]  END;
    origin ← IF dataPtr.stopping THEN GlobalOrigin+WordLength ELSE GlobalOrigin;
    AssignImports[xProfile, 0, 256*WordLength];
    origin ← AssignVars[vProfile, origin, FrameLimit];
    length ← MAX[origin, GlobalOrigin+WordLength];
    CheckFrameOverflow[vProfile];  ReleaseProfile[vProfile];
    CheckFrameOverflow[xProfile];  ReleaseProfile[xProfile];
    RETURN
    END;


  CheckBlock: PUBLIC PROCEDURE [bti: BTIndex] =
    BEGIN

    CheckVar: VarScan =
      BEGIN
      saveIndex: CARDINAL = dataPtr.textIndex;
      node: Tree.Index = LOOPHOLE[seb[sei].idValue];
      IF node # Tree.NullIndex
	THEN
	  BEGIN
	  dataPtr.textIndex ← tb[node].info;
	  IF seb[sei].idInfo = 0 THEN Log.WarningSei[unusedId, sei];
	  END;
      dataPtr.textIndex ← saveIndex;
      END;

    GenCtxVars[bb[bti].localCtx, CheckVar, FALSE];
    END;

  LayoutBlock: PUBLIC PROCEDURE [bti: BTIndex, origin: CARDINAL] RETURNS [length: CARDINAL] =
    BEGIN
    vProfile: Profile;
    vI: CARDINAL;

    CountVar: VarScan = BEGIN vI ← vI + 1 END;

    CountProc: PROCEDURE [bti: CBTIndex] =
      BEGIN
      IF bb[bti].info.mark = Internal THEN vI ← vI + 1;
      END;

    InsertVar: VarScan =
      BEGIN
      nW: CARDINAL;
      vProfile[vI] ← [link: [symbol[sei]], nRefs: seb[sei].idInfo]; vI ← vI+1;
      nW ← (BitsForType[seb[sei].idType] + WordFill)/WordLength;
      seb[sei].idInfo ← nW*WordLength;  seb[sei].idValue ← 0;
      END;

    InsertProc: PROCEDURE [bti: CBTIndex] =
      BEGIN
      IF bb[bti].info.mark = Internal
	THEN
	  BEGIN
	  vProfile[vI] ← VarInfo[
	    link: [body[bti]],
	    nRefs: WITH bb[bti] SELECT FROM Inner=>frameOffset, ENDCASE=>0];
	  vI ← vI+1;
	  END;
      END;

    vI ← 0;  GenCtxVars[bb[bti].localCtx, CountVar, FALSE];
    IF bb[bti].level > lG THEN GenBodyProcs[bti, CountProc];
    vProfile ← AllocateProfile[vI];
    vI ← 0;  GenCtxVars[bb[bti].localCtx, InsertVar, FALSE];
    IF bb[bti].level > lG THEN GenBodyProcs[bti, InsertProc];
    SortProfile[vProfile];
    length ← AssignVars[vProfile, origin, FrameLimit];
    CheckFrameOverflow[vProfile];  ReleaseProfile[vProfile];
    RETURN
    END;


  LayoutInterface: PUBLIC PROCEDURE [bti: CBTIndex] RETURNS [nEntries: CARDINAL] =
    BEGIN
    sei: ISEIndex;
    epN: CARDINAL;
    epN ← 0;
    FOR sei ← ctxb[bb[bti].localCtx].seList, NextSe[sei] UNTIL sei = SENull
      DO
      IF LinkMode[sei] # manifest
	THEN
	  BEGIN
	  seb[sei].linkSpace ← TRUE;
	  seb[sei].idValue ← epN;  epN ← epN + 1;
	  END;
      ENDLOOP;
    IF (nEntries←epN) > EntryLimit
      THEN Log.ErrorN[interfaceEntries, nEntries-EntryLimit];
    RETURN
    END;


  CheckFrameOverflow: PROCEDURE [profile: Profile] =
    BEGIN
    i: INTEGER;
    FOR i IN [0 .. LENGTH[profile])
      DO
      WITH profile[i].link SELECT FROM
	symbol =>  Log.ErrorSei[addressOverflow, index];
	body =>  Log.ErrorSei[addressOverflow, bb[index].id];
	ENDCASE;
      ENDLOOP;
    END;


  Align: PROCEDURE [offset: CARDINAL, item: VarLink] RETURNS [CARDINAL] =
    BEGIN
    RETURN [WITH item SELECT FROM
      body =>
	(offset+WordLength)/(4*WordLength)*(4*WordLength) + (2*WordLength),
      symbol =>
	SELECT XferMode[seb[index].idType] FROM
	  port =>
	   (offset+WordLength)/(4*WordLength)*(4*WordLength) + (2*WordLength),
	  ENDCASE => offset,
      ENDCASE => offset]
    END;

  BitWidth: PROCEDURE [item: VarLink] RETURNS [CARDINAL] =
    BEGIN
    RETURN [WITH item SELECT FROM
      symbol => seb[index].idInfo,
      body => WordLength,
      ENDCASE => 0]
    END;

  AssignBase: PROCEDURE [item: VarLink, base: CARDINAL] =
    BEGIN
    WITH item SELECT FROM
      symbol =>
	BEGIN
	sei: ISEIndex = index;
        seb[sei].idValue ← BitAddress[wd:base/WordLength, bd:0];
        seb[sei].mark4 ← TRUE;
	END;
      body =>
	BEGIN
	bti: CBTIndex = index;
	WITH bb[bti] SELECT FROM
	  Inner =>  frameOffset ← base/WordLength;
	  ENDCASE =>  ERROR;
	END;
      ENDCASE;
    END;

  AssignVars: PROCEDURE [profile: Profile, origin, limit: CARDINAL] RETURNS [CARDINAL] =
    BEGIN
    start, base, length, remainder, delta: CARDINAL;
    i, j, next: INTEGER;
    t: VarLink;
    found, skips: BOOLEAN;
    next ← 0;  start ← origin;  remainder ← limit - origin;
    WHILE next < LENGTH[profile]
      DO
      i ← next;  found ← skips ← FALSE;
      WHILE ~found AND i < LENGTH[profile]
	DO
	IF (t ← profile[i].link) # [empty[]]
	  THEN
	    BEGIN
	    base ← Align[start, t];  length ← BitWidth[t];
	    delta ← base - start;
	    IF length + delta <= remainder
	      THEN
	        BEGIN
	        subBase, subLength, limit: CARDINAL;
		nRefs: CARDINAL;
		nRefs ← 0;  subBase ← start;  limit ← base + length;
	        FOR j ← i+1, j+1 WHILE j < LENGTH[profile] AND subBase < limit
	          DO
	          IF profile[j].link # [empty[]]
	            THEN
	              BEGIN
		      subLength ← BitWidth[profile[j].link];
		      subBase ← Align[subBase, profile[j].link] + subLength;
	              IF subBase > limit THEN EXIT;
	              nRefs ← nRefs + profile[j].nRefs;
	              END;
	          ENDLOOP;
	        IF nRefs <= profile[i].nRefs OR ~dataPtr.switches['s]
	          THEN
		    BEGIN
		    found ← TRUE;
		    AssignBase[t, base];  profile[i].link ← [empty[]];
		    IF base # start AND dataPtr.switches['s]
		      THEN  [] ← AssignVars[profile, start, base];
		    start ← limit;
		    remainder ← remainder - (length+delta);
		    END
	          ELSE IF ~skips THEN  BEGIN  skips ← TRUE;  next ← i  END;
	        END;
	    END;
	i ← i+1;
	IF ~skips THEN next ← i;
	ENDLOOP;
      ENDLOOP;
    RETURN [start]
    END;

  AssignImports: PROCEDURE [profile: Profile, origin, limit: CARDINAL] =
    BEGIN
    nProcs: CARDINAL;
    next: CARDINAL;
    i, j: CARDINAL;
    t: VarLink;
    v: VarInfo;
    i ← nProcs ← LENGTH[profile];
    UNTIL i = 0
      DO
      i ← i-1;  t ← profile[i].link;
      WITH t SELECT FROM
	symbol =>
	  IF XferMode[seb[index].idType] # procedure
	    THEN
	      BEGIN
	      nProcs ← nProcs-1;  v ← profile[i];
	      FOR j IN [i..nProcs) DO profile[j] ← profile[j+1] ENDLOOP;
	      profile[nProcs] ← v;
	      END;
	ENDCASE;
      ENDLOOP;
    -- the xfer frame fragment begins at origin
      dataPtr.linkBase ← origin/WordLength;
      CompilerUtil.AppendBCDWord[dataPtr.linkCount ← LENGTH[profile]];
    i ← LENGTH[profile];
    next ← MIN[origin + LENGTH[profile]*WordLength, limit];
    UNTIL i = 0 OR next = origin
      DO
      i ← i-1;  t ← profile[i].link;  profile[i].link ← [empty[]];
      IF ~dataPtr.definitionsOnly
	THEN
	  WITH t SELECT FROM
	    symbol =>
	      BEGIN
	      sei: ISEIndex = index;
	      next ← next - seb[sei].idInfo;
	      CompilerUtil.AppendBCDWord[seb[sei].idValue];
	      seb[sei].idValue ← BitAddress[wd: next/WordLength, bd: 0];
	      seb[sei].linkSpace ← TRUE;
	      END;
	    ENDCASE;
      ENDLOOP;
    END;


 -- parameter record  layout

  LayoutArgs: PUBLIC PROCEDURE [argRecord: RecordSEIndex, origin: CARDINAL, body: BOOLEAN]
      RETURNS [CARDINAL] =
    BEGIN
    w, nW: CARDINAL;
    ctx: CTXIndex;
    sei: ISEIndex;
    w ← origin;
    IF argRecord # SENull
      THEN
	BEGIN  ctx ← seb[argRecord].fieldCtx;
	FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
	  DO
	  nW ← (BitsForType[seb[sei].idType] + WordFill)/WordLength;
	  IF nW = 0 THEN Log.ErrorSei[sizeClash, sei];
	  IF ~body
	    THEN
	      BEGIN
	      seb[sei].idInfo ← nW*WordLength;
	      seb[sei].idValue ← BitAddress[wd:w, bd:0];
	      END;
	  w ← w + nW;
	  ENDLOOP;
	END;
    RETURN [w]
    END;


 -- record layout

  ScanVariants: PROCEDURE
      [caseCtx: CTXIndex, proc: PROCEDURE [RecordSEIndex] RETURNS [BOOLEAN]]
      RETURNS [BOOLEAN] =
    BEGIN
    sei: ISEIndex;
    rSei: SEIndex;
    FOR sei ← ctxb[caseCtx].seList, NextSe[sei] UNTIL sei = SENull
      DO
      rSei ← seb[sei].idInfo;
      WITH variant: seb[rSei] SELECT FROM
	cons =>
	  WITH variant SELECT FROM
	    record => IF proc[LOOPHOLE[rSei]] THEN RETURN [TRUE];
	    ENDCASE => ERROR;
	ENDCASE =>  NULL;	-- skip multiple identifiers
      ENDLOOP;
    RETURN [FALSE]
    END;

  LayoutFields: PUBLIC PROCEDURE [rSei: RecordSEIndex, offset: CARDINAL] =
    BEGIN
    MaxRecordSize: CARDINAL = LAST[CARDINAL]/WordLength + 1;
    w, b: CARDINAL;
    lastFillable: BOOLEAN;
    lastSei: ISEIndex;

    AssignField: PROCEDURE [sei: ISEIndex] =
      BEGIN  OPEN id: seb[sei];
      n, nW, nB: CARDINAL;
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[LOOPHOLE[id.idValue, Tree.Index]].info;
      n ← BitsForType[id.idType];
      nW ← n/WordLength;  nB ← n MOD WordLength;
      IF nW > 0 AND nB # 0 THEN  BEGIN  nW ← nW+1;  nB ← 0  END;
      IF (nW > 0 OR b+nB > WordLength OR n = 0) AND b # 0
	THEN  BEGIN  w ← w+1;  b ← 0  END;
      dataPtr.textIndex ← saveIndex;
      IF b = 0 AND lastFillable THEN FillWord[lastSei];
      IF w >= MaxRecordSize THEN Log.ErrorSei[addressOverflow, sei];
      id.idInfo ← nW*WordLength + nB;
      id.idValue ← BitAddress[wd:w, bd:b];
      lastSei ← sei;  lastFillable ← (nW = 0 AND n # 0);
      w ← w + nW;  b ← b + nB;
      IF b >= WordLength THEN  BEGIN  w ← w+1;  b ← b - WordLength  END;
--    IF (IF b=0 THEN w ELSE w+1) >= MaxRecordSize
-- 	THEN Log.ErrorSei[addressOverflow, sei];
      END;

    FillWord: PROCEDURE [sei: ISEIndex] =
      BEGIN
      t: BitAddress = seb[sei].idValue;
      width: CARDINAL = WordLength - t.bd;
      IF seb[rSei].machineDep AND width # seb[sei].idInfo
	THEN Log.WarningSei[recordGap, sei];
      seb[sei].idInfo ← width;
      END;

    FindFit: PROCEDURE [vSei: RecordSEIndex] RETURNS [BOOLEAN] =
      BEGIN
      sei: ISEIndex;
      type: CSEIndex;
      sei ← ctxb[seb[vSei].fieldCtx].seList;
      IF sei = SENull THEN RETURN [FALSE];
      type ← UnderType[seb[sei].idType];
      WITH seb[type] SELECT FROM
	union =>
	  IF controlled
	    THEN   sei ← tagSei
	    ELSE RETURN [ScanVariants[caseCtx, FindFit]];
	ENDCASE => NULL;
      RETURN [BitsForType[seb[sei].idType] + b <= WordLength]
      END;

    vOrigin: CARDINAL;
    maxLength: CARDINAL;

    AssignVariant: PROCEDURE [vSei: RecordSEIndex] RETURNS [BOOLEAN] =
      BEGIN
      LayoutFields[vSei, vOrigin];
      maxLength ← MAX[seb[vSei].length, maxLength];
      RETURN [FALSE]
      END;

    eqLengths: BOOLEAN;
    padEnd: CARDINAL;

    PadVariant: PROCEDURE [vSei: RecordSEIndex] RETURNS [BOOLEAN] =
      BEGIN
      sei, fillSei: ISEIndex;
      type: CSEIndex;
      fillOrigin, currentEnd: CARDINAL;
      t: BitAddress;
      ctx: CTXIndex = seb[vSei].fieldCtx;
      fillSei ← ISENull;
      FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
	DO
	IF LOOPHOLE[seb[sei].idValue, BitAddress].wd # w THEN EXIT;
	fillSei ← sei;
	ENDLOOP;
      IF fillSei # SENull
	THEN
	  BEGIN
	  t ← seb[fillSei].idValue;  fillOrigin ← t.wd*WordLength + t.bd;
	  currentEnd ← fillOrigin + seb[fillSei].idInfo;
	  IF currentEnd < padEnd AND (currentEnd # 0 OR padEnd < WordLength)
	    THEN
	      BEGIN
	      type ← UnderType[seb[fillSei].idType];
	      WITH seb[type] SELECT FROM
		union =>
		  BEGIN
		  saveLastSei: ISEIndex = lastSei;
		  IF controlled THEN lastSei ← tagSei;	-- for messages only
		  [] ← ScanVariants[caseCtx, PadVariant];
		  lastSei ← saveLastSei;
		  END;
		ENDCASE =>
		  IF seb[rSei].machineDep
		    THEN Log.WarningSei[recordGap, fillSei];
	      seb[fillSei].idInfo ←  padEnd - fillOrigin; 
	      END;
	  END
	ELSE
	  IF vOrigin < padEnd AND (vOrigin # 0 OR padEnd < WordLength)
	    THEN
	      BEGIN
	      IF seb[rSei].machineDep THEN Log.WarningSei[recordGap, lastSei];
	      fillSei ← MakeCtxSe[HTNull, CTXNull];
	      seb[fillSei].public ← TRUE;  seb[fillSei].extended ← FALSE;
	      seb[fillSei].constant ← seb[fillSei].immutable ← FALSE;
	      seb[fillSei].linkSpace ← FALSE;
	      seb[fillSei].idType ← dataPtr.idANY;
	      seb[fillSei].idValue ← BitAddress[wd:w, bd:b];
	      seb[fillSei].idInfo ← padEnd - vOrigin;
	      seb[fillSei].mark3 ← seb[fillSei].mark4 ← TRUE;
	      WITH seb[fillSei] SELECT FROM
		linked => link ← ctxb[ctx].seList;
		ENDCASE => ERROR;
	      ctxb[ctx].seList ← fillSei;
	      END;
      seb[vSei].length ← MIN[
		maxLength,
		(seb[vSei].length + WordFill)/WordLength * WordLength];
      IF seb[vSei].length # maxLength THEN eqLengths ← FALSE;
      RETURN [FALSE]
      END;

    sei: ISEIndex;
    type: CSEIndex;
    ctx: CTXIndex = seb[rSei].fieldCtx;
    w ← offset/WordLength;  b ← offset MOD WordLength;
    lastFillable ← FALSE;  lastSei ← ISENull;
    FOR sei ← ctxb[ctx].seList, NextSe[sei] UNTIL sei = SENull
      DO
      IF ~seb[sei].constant
	THEN
	  BEGIN
	  type ← UnderType[seb[sei].idType];
	  WITH seb[type] SELECT FROM
	    union =>
	      BEGIN
	      IF ~controlled
		THEN  seb[sei].idValue ← BitAddress[wd:w, bd:b]
		ELSE
		  BEGIN
		  AssignField[tagSei];
		  seb[sei].idValue ← seb[tagSei].idValue;
		  END;
	      IF lastFillable AND b # 0 AND ~ScanVariants[caseCtx, FindFit]
		THEN  BEGIN  FillWord[lastSei];  w ← w+1;  b ← 0  END;
	      maxLength ← vOrigin ← w*WordLength + b;
	      [] ← ScanVariants[caseCtx, AssignVariant];
	      padEnd ← IF maxLength < WordLength
		THEN maxLength
		ELSE MAX[(vOrigin + WordFill)/WordLength, 1]*WordLength;
	      eqLengths ← TRUE;
	      [] ← ScanVariants[caseCtx, PadVariant];
	      equalLengths ← eqLengths;
	      seb[sei].idInfo ←
		(maxLength - vOrigin) +
		  (IF controlled THEN seb[tagSei].idInfo ELSE 0);
	      w ← maxLength/WordLength;  b ← maxLength MOD WordLength;
	      lastFillable ← FALSE;
	      END;
	    ENDCASE =>  AssignField[sei];
	END;
      ENDLOOP;
    IF lastFillable AND b # 0 AND w > 0
      THEN  BEGIN  FillWord[lastSei];  b ← 0;  w ← w + 1  END;
    seb[rSei].length ← w*WordLength + b;
    END;

  END.