-- file Pass4B.Mesa
-- last modified by Satterthwaite, September 10, 1982 1:12 pm
-- last modified by Sweet, Jan 20, 1981 12:32 PM

DIRECTORY
  Alloc: TYPE USING [Notifier, Top],
  BcdDefs: TYPE USING [
    BCD, CodeDesc, EPIndex, EVIndex, EVRecord, EXPRecord, FTIndex, FTRecord,
    GFTIndex, IMPIndex, IMPRecord, Link, LFIndex, MTRecord, Namee, NameRecord,
    NTRecord, PackedString, RefLitIndex, RFIndex, SGIndex, SGRecord, TFIndex, TMRecord,
    TypeIndex, TYPIndex, TYPRecord, VarIndex, VersionID,
    CTNull, EVNull, FTSelf, LFNull, RFNull, TFNull, NullLink, NullName],
  BcdOps: TYPE USING [BcdBase, EVHandle, NameString],
  ComData: TYPE USING [
    codeSeg, compilerVersion, definitionsOnly, fixupLoc, importCtx, linkCount,
    mainCtx, moduleCtx, mtRoot, mtRootSize, nBodies, nSigCodes, objectVersion,
    ownSymbols, pattern, source, switches, symSeg, table, textIndex],
  CompilerUtil: TYPE USING [
    AppendBCDString, AppendBCDWord, AppendBCDWords, EndBCD, FillBCDPage,
    ReadBCDIndex, ReadBCDOffset, RTTableOut, StartBCD, UpdateBCDWords],
  Copier: TYPE USING [FreeSymbolTable, GetSymbolTable, MapSymbols, UnmapSymbols],
  Environment: TYPE USING [wordsPerPage],
  Heap: TYPE USING [FreeNode, MakeNode],
  Log: TYPE USING [ErrorN, ErrorSei, WarningSei, WarningSubString],
  P4: TYPE USING [OwnGfi, DefaultBasicOps, OperandType],
  Pass4: TYPE USING [resident],
  PrincOps: TYPE USING [MaxNGfi],
  ReplOps: TYPE USING [MatchedBodies, Unmatched],
  Strings: TYPE USING [
    SubString, SubStringDescriptor,
    AppendChar, AppendString, AppendSubString, EqualSubStrings, EquivalentSubStrings],
  Symbols: TYPE USING [
    bodyType, ctxType, mdType, seType,
    Base, HTIndex, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, CTXIndex,
    IncludedCTXIndex, BitAddress, Linkage, MDRecord, MDIndex, BTIndex,
    HTNull, SENull, ISENull, CTXNull, MDNull, BTNull, NullFileIndex,
    OwnMdi, RootBti, typeTYPE],
  SymbolOps: TYPE USING [
    FindString, FirstCtxSe, NextSe, SearchContext, SubStringForHash,
    TypeForm, UnderType, XferMode],
  SymbolTable: TYPE USING [Base],
  SymLiteralOps: TYPE USING [
    RefLitItem,
    DescribeRefLits, DescribeTypes, EnumerateRefLits, EnumerateTypes,
    UTypeId, TypeIndex],
  Tree: TYPE USING [Base, Index, Link, Map, Scan, NullIndex, treeType],
  TreeOps: TYPE USING [GetNode, GetSe, ListLength, ScanList],
  Types: TYPE USING [SymbolTableBase, Assignable, Equivalent];
  
Pass4B: PROGRAM
    IMPORTS
      Alloc, CompilerUtil, Copier, Heap, Log, P4, ReplOps, Strings,
      SymbolOps, SymLiteralOps, TreeOps, Types,
      dataPtr: ComData, passPtr: Pass4
    EXPORTS P4 = {
  OPEN SymbolOps, Symbols;
  
  tb: Tree.Base;	-- tree base address (local copy)
  seb: Symbols.Base;	-- se table base address (local copy)
  ctxb: Symbols.Base;	-- context table base address (local copy)
  mdb: Symbols.Base;	-- body table base address (local copy)
  bb: Symbols.Base;	-- body table base address (local copy)
  
  zone: UNCOUNTED ZONE ← NIL;
  
  BCDNotify: PUBLIC Alloc.Notifier = {
    -- called by allocator whenever table area is repacked
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];  mdb ← base[mdType];
    bb ← base[bodyType]};
    
    
-- shared variables

  bcdHeader: BcdOps.BcdBase;
  BcdHeaderPage: CARDINAL = 1;		-- page 0 is reserved
  bcdOffset, mtOffset: CARDINAL;
  
  nString: BcdOps.NameString;
  
  firstPorted: MDIndex = FIRST[MDIndex] + SIZE[MDRecord];
  lastPorted: MDIndex;	-- im/exported files in [firstPorted..lastPorted)
  
-- service routines

  GFTIndex: TYPE = BcdDefs.GFTIndex;
  EPIndex: TYPE = BcdDefs.EPIndex;
  EPLimit: CARDINAL = LAST[EPIndex]+1;
  VarLimit: CARDINAL = LAST[BcdDefs.VarIndex]+1;
  BcdLink: TYPE = BcdDefs.Link;
  
  OwnGfi: GFTIndex = P4.OwnGfi;
  
  
  GFSlots: PROC [epMax: EPIndex] RETURNS [nGfi: [1..4]] = {
    nGfi ← epMax/EPLimit + 1; RETURN};
    
  MakeEPLink: PUBLIC PROC [ep: CARDINAL, gfi: GFTIndex] RETURNS [BcdLink] = {
    RETURN [[procedure[tag: TRUE, ep: ep MOD EPLimit, gfi: gfi + ep/EPLimit]]]};
    
  MakeFrameLink: PROC [ep: CARDINAL, gfi: GFTIndex] RETURNS [BcdLink] = {
    RETURN [[variable[vtag: var, var: ep MOD VarLimit, vgfi: gfi + ep/VarLimit]]]};
    
  MakeTypeLink: PROC [index: BcdDefs.TYPIndex] RETURNS [BcdLink] = INLINE {
    RETURN [[type[typeID: index, type: TRUE, proc: FALSE]]]};
    
    
  MdiForCtx: PROC [ctx: CTXIndex] RETURNS [MDIndex] = {
    RETURN [WITH c: ctxb[ctx] SELECT FROM
      included => c.module,
      imported => ctxb[c.includeLink].module,
      ENDCASE => OwnMdi]};
      
  PortedCtx: PROC [ctx: CTXIndex] RETURNS [BcdDefs.FTIndex] = {
    RETURN [PortedFile[MdiForCtx[ctx]]]};
      
  PortedFile: PROC [mdi: MDIndex] RETURNS [fti: BcdDefs.FTIndex] = {
    n: CARDINAL;
    IF mdi = OwnMdi THEN fti ← BcdDefs.FTSelf
    ELSE {
      IF mdi IN [firstPorted .. lastPorted) THEN
        n ← LOOPHOLE[mdi-firstPorted, CARDINAL]/SIZE[MDRecord]
      ELSE {
	n ← LOOPHOLE[lastPorted-firstPorted, CARDINAL]/SIZE[MDRecord];
	SwapMdi[mdi, lastPorted];
	lastPorted ← lastPorted + SIZE[MDRecord]};
      fti ← LOOPHOLE[n*SIZE[BcdDefs.FTRecord]]};
    RETURN};
    
  SwapMdi: PROC [mdi1, mdi2: MDIndex] = {
    ctx: IncludedCTXIndex;
    t: MDRecord;
    IF mdi1 # mdi2 THEN {
      FOR ctx ← mdb[mdi1].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull DO
	ctxb[ctx].module ← mdi2 ENDLOOP;
      FOR ctx ← mdb[mdi2].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull DO
	ctxb[ctx].module ← mdi1 ENDLOOP;
      t ← mdb[mdi1];  mdb[mdi1] ← mdb[mdi2];  mdb[mdi2] ← t}};
	
	
  SubString: TYPE = Strings.SubString;
  SubStringDescriptor: TYPE = Strings.SubStringDescriptor;
  
  EnterId: PROC [id: SubString, ignoreCase: BOOLEAN] RETURNS [BcdDefs.NameRecord] = {
    i: CARDINAL;
    desc: SubStringDescriptor;
    s: SubString = @desc;
    t: BcdOps.NameString;
    i ← 0;  s.base ← @nString.string;
    UNTIL i = nString.string.length DO
      s.offset ← i ← i+1;  s.length ← nString.size[i];
      IF (IF ignoreCase
	    THEN Strings.EquivalentSubStrings[id, s]
	    ELSE Strings.EqualSubStrings[id, s]) THEN EXIT;
      i ← i + s.length;
      REPEAT
	FINISHED => {
	  IF nString.string.length + (id.length+1) > nString.string.maxlength THEN {
	    -- rewrite if nString is in table area
	    t ← LOOPHOLE[
	     zone.NEW[StringBody[nString.string.maxlength + MAX[(id.length+1), 32]]]];
	    Strings.AppendString[@t.string, @nString.string];
	    zone.FREE[@nString];  nString ← t};
	  i ← nString.string.length ← nString.string.length + 1;
	  nString.size[i] ← id.length;
	  FOR j: CARDINAL IN [0..id.length) DO
	    Strings.AppendChar[@nString.string, id.base[id.offset+j]] ENDLOOP};
      ENDLOOP;
    RETURN [[i]]};
    
  EnterSymbolId: PROC [sei: ISEIndex] RETURNS [BcdDefs.NameRecord] = {
    s: Strings.SubStringDescriptor;
    SubStringForHash[@s, seb[sei].hash];
    RETURN [EnterId[@s, FALSE]]};
    
  EnterFileId: PROC [mdi: MDIndex] RETURNS [BcdDefs.NameRecord] = {
    s: Strings.SubStringDescriptor;
    extLength: CARDINAL = (".bcd"L).length;
    SubStringForHash[@s, mdb[mdi].fileId];
    IF s.base[s.offset+s.length-1] = '. THEN s.length ← s.length - 1;
    IF s.length > extLength THEN {
      t: Strings.SubStringDescriptor ← [
        base: s.base, offset: s.offset+s.length-extLength, length: extLength];
      ext: Strings.SubStringDescriptor ← [base:".bcd"L, offset:0, length:extLength];
      IF Strings.EquivalentSubStrings[@t, @ext] THEN s.length ← s.length - extLength};
    RETURN [EnterId[@s, TRUE]]};
    
    
-- processing directory entries (to file table)

  ProcessDirectory: PUBLIC Tree.Scan = {

    DirectoryItem: Tree.Scan = {
      node: Tree.Index = TreeOps.GetNode[t];
      sei: ISEIndex = TreeOps.GetSe[tb[node].son[1]];
      type: CSEIndex = UnderType[seb[sei].idType];
      WITH t: seb[type] SELECT FROM
	definition => [] ← PortedCtx[t.defCtx];
	transfer => {
	  bti: BTIndex = seb[sei].idInfo;
	  IF bti # BTNull THEN [] ← PortedCtx[bb[bti].localCtx]};
	ENDCASE};
    
    TreeOps.ScanList[t, DirectoryItem]};
    
    
-- relocating imported control links

  ScanImports: PROC [action: PROC [ISEIndex]] = {
    FOR sei: ISEIndex ← FirstCtxSe[dataPtr.importCtx], NextSe[sei] UNTIL sei = ISENull DO
      action[sei] ENDLOOP};
      
  RelocateImports: PROC [ctx: CTXIndex, gfi: GFTIndex] RETURNS [epMax: EPIndex] = {
    epMax ← 0;
    FOR sei: ISEIndex ← FirstCtxSe[ctx], NextSe[sei] UNTIL sei = ISENull DO
      IF ~seb[sei].constant THEN {
	epN: CARDINAL = seb[sei].idValue;
	seb[sei].idValue ← MakeEPLink[epN, gfi];  epMax ← MAX[epN, epMax]};
      ENDLOOP;
    RETURN};
    
  AssignImports: PUBLIC Tree.Scan = {
    gfi: GFTIndex;
    saveIndex: CARDINAL = dataPtr.textIndex;
    
    ImportItem: PROC [sei: ISEIndex] = {
      node: Tree.Index = seb[sei].idValue;
      type: CSEIndex = UnderType[seb[sei].idType];
      epMax: EPIndex;
      IF node # Tree.NullIndex THEN dataPtr.textIndex ← tb[node].info;
      WITH t: seb[type] SELECT FROM
	definition => {
	  IF ctxb[t.defCtx].seList = ISENull THEN Log.WarningSei[unusedImport, sei];
	  epMax ← RelocateImports[t.defCtx, gfi];
	  gfi ← gfi + (seb[sei].idInfo ← t.nGfi)};
	ref => {seb[sei].idValue ← MakeEPLink[ep:0, gfi:gfi]; gfi ← gfi + 1};
	ENDCASE;
      seb[sei].mark4 ← TRUE};
      
    dataPtr.mtRoot.gfi ← OwnGfi;
    dataPtr.mtRoot.ngfi ← GFSlots[MAX[dataPtr.nBodies, dataPtr.nSigCodes]-1];
    gfi ← bcdHeader.firstdummy ← OwnGfi + PrincOps.MaxNGfi;
    IF ~dataPtr.definitionsOnly THEN ScanImports[ImportItem];
    bcdHeader.nDummies ← gfi - bcdHeader.firstdummy;
    dataPtr.textIndex ← saveIndex};
    
    
-- writing frame fragments (link fragment written by Pass4L)

  ProcessSymLiterals: PUBLIC PROC = {
    offset, length: CARDINAL;
    bcdHeader.rfOffset ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.lfLimit ← LOOPHOLE[bcdHeader.rfOffset - bcdHeader.lfOffset];
    IF ~dataPtr.definitionsOnly THEN {
      rfi: BcdDefs.RefLitIndex ← [0];
      
      AppendLitItem: PROC [SymLiteralOps.RefLitItem] = {
        CompilerUtil.AppendBCDWords[@rfi, SIZE[BcdDefs.RefLitIndex]];
	rfi ← [rfi + 1]};
	
      [offset, length] ← SymLiteralOps.DescribeRefLits[];
      IF length # 0 THEN {
        WITH m: dataPtr.mtRoot SELECT FROM
	  multiple => m.refLiterals ← FIRST[BcdDefs.RFIndex];
	  ENDCASE;
	CompilerUtil.AppendBCDWord[offset];  CompilerUtil.AppendBCDWord[length];
	SymLiteralOps.EnumerateRefLits[AppendLitItem]}};
    bcdHeader.tfOffset ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.rfLimit ← LOOPHOLE[bcdHeader.tfOffset - bcdHeader.rfOffset];
    IF ~dataPtr.definitionsOnly THEN {
      tfi: BcdDefs.TypeIndex ← [0];

      AppendTypeIndex: PROC [canonical: BOOLEAN, type: SEIndex] = {
        CompilerUtil.AppendBCDWords[@tfi, SIZE[BcdDefs.TypeIndex]];
	tfi ← [tfi + 1]};
	
      [offset, length] ← SymLiteralOps.DescribeTypes[];
      IF length # 0 THEN {
        WITH m: dataPtr.mtRoot SELECT FROM
	  multiple => m.types ← FIRST[BcdDefs.TFIndex];
	  ENDCASE;
	CompilerUtil.AppendBCDWord[offset];  CompilerUtil.AppendBCDWord[length];
	SymLiteralOps.EnumerateTypes[AppendTypeIndex]}};
    bcdHeader.tfLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[] - bcdHeader.tfOffset]};
  
  
-- writing import records

  ProcessImports: PUBLIC Tree.Scan = {
    -- N.B. nextGfi must be regenerated to match AssignImports
    nImports: CARDINAL;
    impi: BcdDefs.IMPIndex;
    nextGfi: GFTIndex;
    anyNamed: BOOLEAN;
    
    ProcessSei: PROC [sei, tSei: ISEIndex] = {
      type: CSEIndex = UnderType[seb[sei].idType];
      entry: BcdDefs.IMPRecord ← [
		name: EnterSymbolId[tSei],
		port: interface,
		namedInstance: seb[sei].hash # seb[tSei].hash,
		file: ,
		gfi: nextGfi, ngfi: ];
      WITH t: seb[type] SELECT FROM
	definition => {
	  entry.file ← PortedCtx[t.defCtx];  entry.ngfi ← seb[sei].idInfo;
	  nextGfi ← (seb[sei].idValue ← nextGfi) + seb[sei].idInfo};
	ref => {
	  rType: RecordSEIndex = LOOPHOLE[UnderType[t.refType]];
	  entry.port ← module;
	  entry.file ← PortedCtx[seb[rType].fieldCtx];  entry.ngfi ← 1;
	  nextGfi ← nextGfi + 1};
	ENDCASE;
      nImports ← nImports + 1;
      IF entry.namedInstance THEN anyNamed ← TRUE;
      CompilerUtil.AppendBCDWords[@entry, SIZE[BcdDefs.IMPRecord]]};
      
    sei: ISEIndex;	-- updated by ImportItem
    
    ImportItem: Tree.Scan = {
      node: Tree.Index = TreeOps.GetNode[t];
      ProcessSei[sei, TreeOps.GetSe[tb[node].son[2]]];
      sei ← NextSe[sei]};
      
    NameItem: Tree.Scan = {
      node: Tree.Index = TreeOps.GetNode[t];
      sei: ISEIndex = TreeOps.GetSe[tb[node].son[1]];
      tSei: ISEIndex = TreeOps.GetSe[tb[node].son[2]];
      entry: BcdDefs.NTRecord;
      IF  seb[sei].hash # seb[tSei].hash THEN {
	entry ← [name: EnterSymbolId[sei], item: BcdDefs.Namee[import[impi]]];
	CompilerUtil.AppendBCDWords[@entry, SIZE[BcdDefs.NTRecord]]};
      impi ← impi + SIZE[BcdDefs.IMPRecord]};
      
    offset: CARDINAL;
    bcdHeader.impOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    nImports ← 0;  impi ← FIRST[BcdDefs.IMPIndex];
    nextGfi ← bcdHeader.firstdummy;  anyNamed ← FALSE;
    IF ~dataPtr.definitionsOnly THEN {
      sei ← FirstCtxSe[dataPtr.importCtx];
      TreeOps.ScanList[t, ImportItem];
      UNTIL sei = ISENull DO ProcessSei[sei, sei]; sei ← NextSe[sei] ENDLOOP};
    bcdHeader.nImports ← nImports;
    bcdHeader.impLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    bcdHeader.ntOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    IF anyNamed THEN TreeOps.ScanList[t, NameItem];	-- matches importCtx prefix
    bcdHeader.ntLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset]};
    
    
-- writing export records

  maxEVLength: CARDINAL;
  evList: BcdOps.EVHandle;
  
  EnterEVOffset: PROC [offset: CARDINAL] RETURNS [index: CARDINAL] = {
    IF offset = 0 THEN index ← 0
    ELSE
      FOR index IN [1 .. evList.length] DO
	IF offset = evList.offsets[index] THEN EXIT;
	REPEAT
	  FINISHED => {
	    index ← evList.length ← evList.length + 1;
	    IF index <= maxEVLength THEN evList.offsets[index] ← offset};
	ENDLOOP;
    RETURN};
    
    
  TypeMap: TYPE = RECORD [SEQUENCE length: CARDINAL OF
    RECORD [opaque: BcdDefs.TMRecord, concrete: BcdDefs.TYPRecord]];
  typeMap: LONG POINTER TO TypeMap;
  mapIndex: CARDINAL;
  typeIndex: BcdDefs.TYPIndex;
  
  EnterType: PROC [mdi: MDIndex, offset: CARDINAL, sei: ISEIndex]
      RETURNS [typeId: BcdDefs.TYPIndex] = {
    entry: BcdDefs.TYPRecord = MakeTypeId[sei];
    IF typeMap = NIL OR mapIndex >= typeMap.length THEN AdjustTypeMap[8];
    FOR i: CARDINAL IN [0..mapIndex) DO
      IF typeMap[i].concrete = entry THEN GO TO matched;
      REPEAT
	matched => typeId ← typeMap[i].opaque.map;
	FINISHED => {typeId ← typeIndex; typeIndex ← typeIndex + SIZE[BcdDefs.TYPRecord]};
      ENDLOOP;
    typeMap[mapIndex] ← [
	opaque: [version: mdb[mdi].stamp, offset: offset, map: typeId],
	concrete: entry];
    mapIndex ← mapIndex + 1;
    RETURN};
    
  MakeTypeId: PROC [id: ISEIndex] RETURNS [BcdDefs.TYPRecord] = {
    sei: ISEIndex ← id;
    next: SEIndex;
    mdi: MDIndex;
    DO
      next ← seb[sei].idInfo;
      WITH seb[next] SELECT FROM
	id => sei ← LOOPHOLE[next];
	ENDCASE => EXIT;
      ENDLOOP;
    mdi ← MdiForCtx[seb[sei].idCtx];
    RETURN [[id: [seb[sei].idValue], version: mdb[mdi].stamp]]};
    
  AdjustTypeMap: PROC [delta: CARDINAL] = {
    oldN: CARDINAL = IF typeMap = NIL THEN 0 ELSE typeMap.length;
    newMap: LONG POINTER TO TypeMap = zone.NEW[TypeMap[oldN+delta]];
    FOR i: CARDINAL IN [0 .. oldN) DO newMap[i] ← typeMap[i] ENDLOOP;
    IF typeMap # NIL THEN zone.FREE[@typeMap];
    typeMap ← newMap};
    
  WriteTypeTable: PROC = {
    i, offset: CARDINAL;
    next: BcdDefs.TYPIndex ← FIRST[BcdDefs.TYPIndex];
    bcdHeader.typOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    FOR i IN [0 .. mapIndex) DO
      FOR j: CARDINAL IN [0..i) DO
	IF typeMap[i].opaque.map = typeMap[j].opaque.map THEN EXIT
	REPEAT
	  FINISHED => {
	    CompilerUtil.AppendBCDWords[@typeMap[i].concrete, SIZE[BcdDefs.TYPRecord]];
	    next ← next + SIZE[BcdDefs.TYPRecord]};
	ENDLOOP;
      ENDLOOP;
    bcdHeader.typLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    bcdHeader.tmOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    FOR i IN [0 .. mapIndex) DO
      CompilerUtil.AppendBCDWords[@typeMap[i].opaque, SIZE[BcdDefs.TMRecord]];
      ENDLOOP;
    bcdHeader.tmLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset]};
    
    
  ExportId: Tree.Map = {
    expType: CSEIndex = P4.OperandType[t];
    ctx: IncludedCTXIndex;
    iBase: Types.SymbolTableBase;
    id, sei, iSei: ISEIndex;
    epN: CARDINAL;
    used: BOOLEAN;
    id ← TreeOps.GetSe[t];
    WITH v: seb[expType] SELECT FROM
      definition => {
	ctx ← LOOPHOLE[v.defCtx];
	iBase ← Copier.GetSymbolTable[ctxb[ctx].module];
	IF iBase # NIL THEN {
	  BEGIN
	  header: BcdDefs.EXPRecord ← [
		  name: EnterSymbolId[id],
		  size: 0,
		  port: interface,
		  namedInstance: FALSE,
		  typeExported: FALSE,
		  file: PortedCtx[v.defCtx],
		  links: ];
	  FOR iSei ← iBase.FirstCtxSe[ctxb[ctx].map], iBase.NextSe[iSei] UNTIL iSei = ISENull DO
	    SELECT iBase.LinkMode[iSei] FROM
	      val, ref => header.size ← header.size + 1;
	      type => {header.typeExported ← TRUE; header.size ← header.size + 1};
	      ENDCASE;
	    ENDLOOP;
	  CompilerUtil.AppendBCDWords[@header, SIZE[BcdDefs.EXPRecord]];
	  END;
	  used ← FALSE;  epN ← 0;
	  FOR iSei ← iBase.FirstCtxSe[ctxb[ctx].map], iBase.NextSe[iSei] UNTIL iSei = ISENull DO
	    mode: Linkage = iBase.LinkMode[iSei];
	    link: BcdLink ← BcdDefs.NullLink;
	      BEGIN
	      ss: SubStringDescriptor;
	      hti: HTIndex;
	      iBase.SubStringForHash[@ss, iBase.seb[iSei].hash];
	      hti ← FindString[@ss];
	      IF hti = HTNull THEN sei ← ISENull
	      ELSE {
		sei ← SearchContext[hti, dataPtr.mainCtx];
		IF sei = ISENull THEN sei ← SearchContext[hti, dataPtr.moduleCtx]};
	      END;
	    IF sei # SENull THEN {
	      public: BOOLEAN = seb[sei].public;
	      iType: CSEIndex = iBase.UnderType[iBase.seb[iSei].idType];
	      SELECT mode FROM
		val => {
		  IF ~Types.Assignable[
			[iBase, iType], [dataPtr.ownSymbols, UnderType[seb[sei].idType]]] THEN {
		    IF public THEN Log.ErrorSei[exportClash,sei]}
		  ELSE IF ~public AND seb[sei].idCtx = dataPtr.mainCtx THEN
		    Log.WarningSei[privateExport, sei];
		  IF public THEN {
		    IF ~seb[sei].constant OR seb[sei].extended THEN Log.ErrorSei[varExport, sei];
		    link ← IF XferMode[seb[sei].idType] = program
				THEN MakeFrameLink[ep:EnterEVOffset[0], gfi:OwnGfi]
				ELSE seb[sei].idValue}};
		ref => {
		  iTarget: CSEIndex ← iType;
		  iConst: BOOLEAN ← iBase.seb[iSei].immutable;
		  WITH t: iBase.seb[iType] SELECT FROM
		    ref =>
		      IF t.var THEN {iTarget ← iBase.UnderType[t.refType]; iConst ← t.readOnly}; 
		    ENDCASE;
		  IF ~Types.Equivalent[
			[iBase, iTarget], [dataPtr.ownSymbols, UnderType[seb[sei].idType]]] THEN {
		    IF public THEN Log.ErrorSei[exportClash,sei]}
		  ELSE IF ~public AND seb[sei].idCtx = dataPtr.mainCtx THEN
		    Log.WarningSei[privateExport, sei];
		  IF public THEN {
		    SELECT TRUE FROM
		      seb[sei].constant => Log.ErrorSei[varExport, sei];
		      seb[sei].immutable AND ~iConst => Log.ErrorSei[exportClash, sei];
		      ENDCASE;
		  link ← MakeFrameLink[
			   ep: EnterEVOffset[LOOPHOLE[seb[sei].idValue, BitAddress].wd],
			   gfi: OwnGfi]}};
		type =>
		  IF seb[sei].idType # typeTYPE OR TypeForm[sei] = opaque THEN {
		    IF public THEN Log.ErrorSei[exportClash, sei]}
		  ELSE {
		    iValue: CSEIndex = iBase.UnderType[iSei];
		    IF (~public AND seb[sei].idCtx = dataPtr.mainCtx) THEN
		      Log.WarningSei[privateExport, sei]
		    ELSE
		      WITH it: iBase.seb[iValue] SELECT FROM
			opaque =>
			  IF it.lengthKnown AND ~P4.DefaultBasicOps[sei, it.length] THEN
			    Log.ErrorSei[exportAttr, sei];
			ENDCASE => ERROR;
		    IF public THEN {
		      link ← MakeTypeLink[EnterType[ctxb[ctx].module, epN, sei]];
		      bcdHeader.typeExported ← TRUE}};
		manifest =>
		  IF public
		    AND (seb[sei].idType # typeTYPE OR iBase.seb[iSei].idType # typeTYPE) THEN
		     Log.WarningSei[voidExport, sei];
		ENDCASE};
	    IF link # BcdDefs.NullLink THEN used ← TRUE;
	    IF mode # manifest THEN {CompilerUtil.AppendBCDWord[link]; epN ← epN + 1};
	    ENDLOOP;
	Copier.FreeSymbolTable[iBase];
	IF ~used THEN Log.WarningSei[unusedExport, id]}};
      ENDCASE;
    RETURN [t]};
    
  ExportItem: Tree.Scan = {
    node: Tree.Index = TreeOps.GetNode[t];
    saveIndex: CARDINAL = dataPtr.textIndex;
    dataPtr.textIndex ← tb[node].info;
    tb[node].son[2] ← ExportId[tb[node].son[2]];
    dataPtr.textIndex ← saveIndex};
    
  ProcessExports: PUBLIC Tree.Map = {
    offset: CARDINAL;
    bcdHeader.nExports ← TreeOps.ListLength[t];
    bcdHeader.expOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    maxEVLength ← PrincOps.MaxNGfi*VarLimit - 1;
    evList ← Heap.MakeNode[zone, SIZE[BcdDefs.EVRecord]+maxEVLength];
    evList↑ ← [length:0, offsets:];
    typeIndex ← FIRST[BcdDefs.TYPIndex];  mapIndex ← 0;  typeMap ← NIL;
    TreeOps.ScanList[t, ExportItem];
    bcdHeader.expLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    bcdHeader.evOffset ← offset ← CompilerUtil.ReadBCDOffset[];
    IF evList.length > maxEVLength THEN Log.ErrorN[exportedVars, evList.length-maxEVLength];
    IF evList.length = 0 THEN dataPtr.mtRoot.variables ← BcdDefs.EVNull
    ELSE {
      dataPtr.mtRoot.ngfi ← MAX[dataPtr.mtRoot.ngfi, evList.length/VarLimit+1];
      dataPtr.mtRoot.variables ← FIRST[BcdDefs.EVIndex];
      CompilerUtil.AppendBCDWords[evList,
	  SIZE[BcdDefs.EVRecord] + MIN[evList.length, maxEVLength]]};
    bcdHeader.evLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-offset];
    Heap.FreeNode[zone, evList];
    WriteTypeTable[];
    IF typeMap # NIL THEN zone.FREE[@typeMap];
    RETURN [t]};
    

-- initialization/finalization

  ProcessFiles: PROC = {
    ftEntry: BcdDefs.FTRecord;
    mdi: MDIndex;
    limit: MDIndex = (dataPtr.table).Top[mdType];

    EnterCanonicalFile: PROC [canonical: BOOLEAN, type: SEIndex] = {
      IF ~canonical THEN {
        mdi: MDIndex;
	[mdi, ] ←  SymLiteralOps.UTypeId[type];
	IF mdi # MDNull THEN [] ← PortedFile[mdi]}};
	
    offset: CARDINAL = CompilerUtil.ReadBCDOffset[];
    FOR mdi ← lastPorted, mdi + SIZE[MDRecord] UNTIL mdi = limit DO
      IF mdb[mdi].file # NullFileIndex THEN
        [] ← PortedFile[mdi];	-- add any files opened during compilation
      ENDLOOP;
    bcdHeader.ftOffset ← offset;
    SymLiteralOps.EnumerateTypes[EnterCanonicalFile];
    FOR mdi ← firstPorted, mdi + SIZE[MDRecord] UNTIL mdi = lastPorted DO
      ftEntry ← [name: EnterFileId[mdi], version: mdb[mdi].stamp];
      CompilerUtil.AppendBCDWords[@ftEntry, SIZE[BcdDefs.FTRecord]];
      ENDLOOP;
    bcdHeader.ftLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[] - offset]};
    
    
  InitBCD: PUBLIC PROC [ids: Tree.Link, scratchZone: UNCOUNTED ZONE] = {
    OPEN BcdDefs;
    nIds: CARDINAL;
    zone ← scratchZone;
    nIds ← TreeOps.ListLength[ids];
    IF nIds > 1 AND ~dataPtr.definitionsOnly THEN {Log.ErrorN[listLong, nIds-1]; nIds ← 1};
    lastPorted ← firstPorted;
    nString ← LOOPHOLE[zone.NEW[StringBody[64]]];
    -- allocate the null name
      nString.string.length ← BcdDefs.NullName;
      nString.size[BcdDefs.NullName] ← 0;
    mdb[OwnMdi].stamp ← dataPtr.objectVersion;	-- update from DIRECTORY processing
    CompilerUtil.StartBCD[];
    bcdHeader ← zone.NEW[BCD];
      bcdHeader.versionIdent ← VersionID;
      bcdHeader.version ← dataPtr.objectVersion;
      bcdHeader.creator ← dataPtr.compilerVersion;
      bcdHeader.sourceVersion ← dataPtr.source.version;
      bcdHeader.nConfigs ← 0;
      bcdHeader.nModules ← nIds;
      bcdHeader.nImports ← bcdHeader.nExports ← 0;
      bcdHeader.definitions ← dataPtr.definitionsOnly;
      bcdHeader.typeExported ← FALSE;
      bcdHeader.repackaged ← bcdHeader.tableCompiled ← FALSE;
      bcdHeader.versions ← FALSE;
      bcdHeader.extended ← TRUE;
      bcdHeader.spare1 ← TRUE;	-- large eval stack
      bcdHeader.spare2 ← FALSE;
      bcdHeader.ctOffset ← 0;  bcdHeader.ctLimit ← LOOPHOLE[0];
      bcdHeader.spOffset ← 0;  bcdHeader.spLimit ← LOOPHOLE[0];
      bcdHeader.fpOffset ← 0;  bcdHeader.fpLimit ← LOOPHOLE[0];
      nString.string.length ← nString.string.length + 1;
      bcdHeader.source ← NameRecord[nString.string.length];
	nString.size[bcdHeader.source] ← dataPtr.source.locator.length;
	Strings.AppendSubString[@nString.string, @dataPtr.source.locator];
    bcdOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDWords[bcdHeader, SIZE[BCD]];
    dataPtr.fixupLoc ← CompilerUtil.ReadBCDIndex[];
    bcdHeader.sgOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDWords[@dataPtr.codeSeg, SIZE[SGRecord]];
    CompilerUtil.AppendBCDWords[@dataPtr.symSeg, SIZE[SGRecord]];
    bcdHeader.mtOffset ← mtOffset ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.sgLimit ← LOOPHOLE[mtOffset - bcdHeader.sgOffset];
    IF dataPtr.definitionsOnly THEN {
      dataPtr.mtRootSize ← SIZE[indirect BcdDefs.MTRecord];
      dataPtr.mtRoot ← Heap.MakeNode[zone, SIZE[indirect BcdDefs.MTRecord]];
      dataPtr.mtRoot.extension ← indirect[links: BcdDefs.LFNull]}
    ELSE {
      dataPtr.mtRootSize ← SIZE[multiple BcdDefs.MTRecord];
      dataPtr.mtRoot ← Heap.MakeNode[zone, SIZE[multiple BcdDefs.MTRecord]];
      dataPtr.mtRoot.extension ← multiple[
          links: FIRST[BcdDefs.LFIndex],
	  refLiterals: BcdDefs.RFNull,
	  types: BcdDefs.TFNull]};
    FOR i: CARDINAL IN [0..nIds) DO
      CompilerUtil.AppendBCDWords[dataPtr.mtRoot, dataPtr.mtRootSize]
      ENDLOOP;
    bcdHeader.lfOffset ← CompilerUtil.ReadBCDOffset[];
    bcdHeader.mtLimit ← LOOPHOLE[bcdHeader.lfOffset-bcdHeader.mtOffset]};
      
  FinishBCD: PUBLIC PROC [ids: Tree.Link] = {
    OPEN BcdDefs;
    PageSize: CARDINAL = Environment.wordsPerPage;
    Alignment: CARDINAL = 4;  -- Code Segments must start at 0 MOD Alignment
    nLinks: CARDINAL = dataPtr.linkCount;
    codeLinks: BOOLEAN = dataPtr.switches['l];
    gfType: RecordSEIndex = bb[RootBti].type;
    -- fill MTRecord
      IF TreeOps.ListLength[ids] > 1 THEN {	-- complete nString now

	EnterId: Tree.Scan = {[] ← EnterSymbolId[TreeOps.GetSe[t]]};

	TreeOps.ScanList[ids, EnterId]};
      dataPtr.mtRoot.name ← EnterSymbolId[bb[RootBti].id];
      dataPtr.mtRoot.namedInstance ← FALSE;
      dataPtr.mtRoot.initial ← ~dataPtr.switches['s];
      dataPtr.mtRoot.file ← dataPtr.codeSeg.file ← dataPtr.symSeg.file ← 
        PortedCtx[dataPtr.mainCtx];
      dataPtr.mtRoot.linkLoc ← IF codeLinks THEN code ELSE frame;
      dataPtr.mtRoot.config ← CTNull;
      dataPtr.mtRoot.code ← CodeDesc[
		sgi: FIRST[SGIndex],
		packed: FALSE, linkspace: codeLinks,
		offset: IF codeLinks AND nLinks # 0
		  THEN (nLinks+1) + (Alignment-1 - (nLinks MOD Alignment))
		  ELSE 0,
		length: 0];	-- will be updated
      dataPtr.mtRoot.sseg ← FIRST[SGIndex] + SIZE[SGRecord];
      dataPtr.mtRoot.frameRefs ← seb[gfType].hints.refField;
      dataPtr.mtRoot.frameType ←
        IF seb[gfType].hints.refField THEN SymLiteralOps.TypeIndex[gfType] ELSE 0;
      dataPtr.mtRoot.framesize ← 0;	-- will be updated
      dataPtr.mtRoot.altoCode ←  FALSE;
      dataPtr.mtRoot.tableCompiled ← FALSE;
      dataPtr.mtRoot.residentFrame ← passPtr.resident;
      dataPtr.mtRoot.boundsChecks ← dataPtr.switches['b];
      dataPtr.mtRoot.nilChecks ← dataPtr.switches['n];
      dataPtr.mtRoot.long ← dataPtr.switches['c];	-- compiled for Cedar
      dataPtr.mtRoot.crossJumped ← dataPtr.switches['j];
      dataPtr.mtRoot.packageable ← TRUE;
    ProcessFiles[];
    bcdHeader.ssOffset ← CompilerUtil.ReadBCDOffset[];
    CompilerUtil.AppendBCDString[@nString.string];
    bcdHeader.ssLimit ← LOOPHOLE[CompilerUtil.ReadBCDOffset[]-bcdHeader.ssOffset];
    IF dataPtr.definitionsOnly THEN bcdHeader.rtPages.relPageBase ← 0
    ELSE {
      CompilerUtil.FillBCDPage[];
      bcdHeader.rtPages.relPageBase ← CompilerUtil.ReadBCDOffset[]/PageSize;
      CompilerUtil.RTTableOut[dataPtr.table]};
    bcdHeader.nPages ← (CompilerUtil.ReadBCDOffset[] + (PageSize-1))/PageSize;
    bcdHeader.rtPages.pages ← bcdHeader.nPages - bcdHeader.rtPages.relPageBase;
    CompilerUtil.UpdateBCDWords[bcdOffset, bcdHeader, SIZE[BCD]];
    IF dataPtr.definitionsOnly AND TreeOps.ListLength[ids] > 1 THEN {
      offset: CARDINAL ← mtOffset;
      saveName: BcdDefs.NameRecord = dataPtr.mtRoot.name;
	
      UpdateMDEntry: Tree.Scan = {
	dataPtr.mtRoot.name ← EnterSymbolId[TreeOps.GetSe[t]];
	CompilerUtil.UpdateBCDWords[offset, dataPtr.mtRoot, dataPtr.mtRootSize];
	offset ← offset + dataPtr.mtRootSize};
	  
      offset ← mtOffset;  TreeOps.ScanList[ids, UpdateMDEntry];
      dataPtr.mtRoot.name ← saveName};
    CompilerUtil.EndBCD[];
    zone.FREE[@nString]; zone.FREE[@bcdHeader]; zone ← NIL};
    

  MatchBCD: PUBLIC PROC RETURNS [matched: BOOLEAN ← FALSE] = {
    oldSymbols: SymbolTable.Base ← Copier.MapSymbols[dataPtr.pattern];
    IF oldSymbols = NIL THEN
      Log.WarningSubString[fileName, @dataPtr.pattern.locator]
    ELSE {
      matched ← ReplOps.MatchedBodies[
	    [oldSymbols, RootBti], [dataPtr.ownSymbols, RootBti]
	! ReplOps.Unmatched => {
	    d: Strings.SubStringDescriptor;
	    id.stb.SubStringForHash[@d, id.stb.HashForSe[id.sei]];
	    SELECT attr FROM
	      strings => Log.WarningSubString[replString, @d];
	      id => Log.WarningSubString[replId, @d];
	      ENDCASE;
	    RESUME}];
      Copier.UnmapSymbols[oldSymbols]}};

  }.