-- VarUtils.mesa  Edited by Sweet, January 16, 1980  1:07 PM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [BYTE, wordlength],
  Code: FROM "code" USING [catchcount, curctxlvl],
  CodeDefs: FROM "codedefs",
  ControlDefs: FROM "controldefs" USING [EPRange, GFTNull,
    ProcDesc, SignalDesc],
  InlineDefs: FROM "inlinedefs" USING [BITAND, BITSHIFT],
  LiteralOps: FROM "literalops" USING [MasterString, Value],
  Literals: FROM "literals" USING [MSTIndex, stType],
  P5: FROM "p5",
  P5L: FROM "p5l",
  P5U: FROM "p5u" USING [FreeChunk, GetChunk],
  Stack: FROM "stack" USING [Above, Forget, KeepOnly, Loc, TempStore, Top],
  SymbolOps: FROM "symbolops" USING [XferMode],
  Symbols: FROM "symbols" USING [BitAddress, bodyType, BTNull, CBTIndex,
    ContextLevel, ctxType, ISEIndex, ISENull, lG, lZ, RecordSEIndex, seType],
  Table: FROM "table" USING [Base, Notifier],
  Tree: FROM "tree" USING [treeType];

VarUtils: PROGRAM
  IMPORTS LCPtr: Code, InlineDefs, LiteralOps, P5, P5U,
    P5L, Stack, SymbolOps
  EXPORTS P5L, CodeDefs =
  BEGIN OPEN CodeDefs, Symbols;

  CPtr: POINTER TO FRAME [Code] = LCPtr;

  wordlength: CARDINAL = AltoDefs.wordlength;
  BYTE: TYPE = AltoDefs.BYTE;

  cb, seb, ctxb, bb, stb: Table.Base;

  VarUtilsNotify: PUBLIC Table.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    ctxb ← base[Symbols.ctxType];
    cb ← base[Tree.treeType];
    bb ← base[Symbols.bodyType];
    stb ← base[Literals.stType];
    RETURN
    END;

  AdjustComponent: PUBLIC PROCEDURE [var: POINTER TO VarComponent,
    rSei: Symbols.RecordSEIndex, fSei: Symbols.ISEIndex, tBits: CARDINAL] =
    BEGIN
    length: CARDINAL = seb[rSei].length;
    first: BOOLEAN = (seb[fSei].idValue = 0);
    delta: CARDINAL;

    IF length < wordlength AND (delta ← tBits - length) # 0 THEN
      BEGIN
      IF first THEN 
	BEGIN
	newB: CARDINAL = var.bSize + delta;
	var.bSize ← newB MOD wordlength;
	var.wSize ← newB / wordlength;
	END
      ELSE P5L.ModComponent[var: var, bd: delta];
      END;
    END;

  AllLoaded: PUBLIC PROCEDURE [r: VarIndex, junkOk: BOOLEAN ← FALSE]
      RETURNS [BOOLEAN] =
    BEGIN -- is completely on stack (there may be stuff above it, tho)
    WITH cb[r] SELECT FROM
      o => WITH vv: var SELECT FROM
	stack => IF (junkOk OR vv.wd = 0) AND vv.bd = 0 THEN
	  BEGIN
	  sti: StackIndex ← IF vv.wd # 0 THEN Stack.Above[vv.sti, vv.wd]
		ELSE vv.sti;
	  loc: StackLocRec ← Stack.Loc[sti, var.wSize];
	  IF loc.tag = onStack THEN RETURN[TRUE];
	  END;
	ENDCASE;
      ENDCASE;
    RETURN[FALSE];
    END;

  ComponentForLex: PUBLIC PROCEDURE [l: Lexeme, allowFields: BOOLEAN ← FALSE]
      RETURNS [VarComponent] = 
    BEGIN
    WITH ll: l SELECT FROM
      bdo => RETURN [P5L.MakeComponent[ll.lexbdoi, allowFields]];
      se => RETURN [P5L.ComponentForSE[ll.lexsei]];
      literal =>
	BEGIN OPEN Literals;
	WITH ll SELECT FROM
	  word => RETURN[
	    [wSize: 1, space: const[d1: LiteralOps.Value[lexlti]]]];
	  string =>
	    BEGIN
	    msti: MSTIndex ← LiteralOps.MasterString[lexsti];
	    RETURN[[wSize: 1, space: faddr[
		wd: stb[msti].info,
	        level: IF stb[msti].local THEN
			 CPtr.curctxlvl - CPtr.catchcount
		       ELSE lG]]];
	    END;
	  ENDCASE;
	END;
      stack => RETURN [[wSize: 1, space: stack[sti: ll.lexsti]]];
      ENDCASE;
    ERROR;
    END;

  ComponentForSE: PUBLIC PROCEDURE [sei: ISEIndex] RETURNS [var: VarComponent] =
    BEGIN
    SELECT TRUE FROM
      sei = ISENull => ERROR;
      seb[sei].linkSpace => 
	BEGIN
	a: BitAddress = seb[sei].idValue;
	var ← [wSize: 1, space: link[wd: a.wd]];
	END;
      seb[sei].constant => SELECT SymbolOps.XferMode[seb[sei].idType] FROM
	procedure =>
	  BEGIN
	  bti: CBTIndex = seb[sei].idInfo;
	  IF bti = BTNull THEN RETURN [[
	    wSize: 1, space: const[d1: seb[sei].idValue]]]
	  ELSE
	    BEGIN
	    WITH bb[bti] SELECT FROM
	      Inner => RETURN [[wSize: 1,
		space: faddr[wd: frameOffset, level: bb[bti].level - 1]]];
	      Outer =>
		BEGIN OPEN ControlDefs;
		p: ProcDesc;
		p.gfi ← entryIndex / EPRange;
		p.ep ← entryIndex MOD EPRange;
		p.tag ← procedure;
		RETURN [[wSize: 1, space: pdesc[p]]];
		END;
	      ENDCASE;
            END;
	  END;
	signal, error =>
	  BEGIN OPEN ControlDefs;
	  desc: SignalDesc ← seb[sei].idValue;
	  IF desc.gfi # GFTNull THEN
	    BEGIN
	    desc.gfi ← desc.gfi-1;
	    RETURN [[wSize: 1, space: pdesc[desc]]];
	    END
	  ELSE RETURN [[wSize: 1, space: const[d1: desc]]];
	  END;
	ENDCASE => ERROR;
      ENDCASE =>
	BEGIN
	a: Symbols.BitAddress ← seb[sei].idValue;
	s: CARDINAL = seb[sei].idInfo;
	
	RETURN [[wSize: s / wordlength, bSize: s MOD wordlength,
          space: frame[
	    wd: a.wd, bd: a.bd,
            immutable: seb[sei].immutable,
            level: ctxb[seb[sei].idCtx].level]]];
	END;
    END;

  CopyLex: PUBLIC PROCEDURE [l: Lexeme] RETURNS [Lexeme] =
    BEGIN
    WITH l SELECT FROM
      bdo => RETURN [[bdo[CopyVarItem[lexbdoi]]]];
      ENDCASE => RETURN [l];
    END;

  CopyToTemp: PUBLIC PROCEDURE [r: VarIndex, tsei: ISEIndex ← ISENull]
      RETURNS [var: VarComponent, sei: ISEIndex] =
    BEGIN -- needs work for non aligned things
    bd, bSize: [0..wordlength);
    wSize, wS: CARDINAL;
    rr: VarIndex;
    
    sei ← tsei;
    [bd: bd, bSize: bSize, wSize: wSize] ← P5L.VarAlignment[r, load];
    wS ← P5L.Words[wSize, bSize];
    IF sei = ISENull THEN
      sei ← P5.GenTempLex[wS].lexsei;
    var ← P5L.ComponentForSE[sei];
    IF wS > 1 THEN
      BEGIN
      var.wSize ← wSize; var.bSize ← bSize;
      WITH vv: var SELECT FROM
        frame => vv.bd ← bd;
        ENDCASE;
      END;
    rr ← P5L.OVarItem[var];
    [] ← P5L.VarVarAssign[rr, r, FALSE];
    END;

  CopyVarItem: PUBLIC PROCEDURE [r: VarIndex] RETURNS [rr: VarIndex] =
    BEGIN
    -- LOOPHOLEs can go away when the compiler gets smarter
    WITH cc: cb[r] SELECT FROM
      o =>
	BEGIN
	tr: OVarIndex ← LOOPHOLE[P5L.GenVarItem[o]];
	rr ← tr;
	cb[tr] ← cc;
        END;
      bo =>
	BEGIN
	tr: BoVarIndex ← LOOPHOLE[P5L.GenVarItem[bo]];
	rr ← tr;
	cb[tr] ← cc;
        END;
      bdo =>
	BEGIN
	tr: BdoVarIndex ← LOOPHOLE[P5L.GenVarItem[bdo]];
	rr ← tr;
	cb[tr] ← cc;
        END;
      ind =>
	BEGIN
	tr: IndVarIndex ← LOOPHOLE[P5L.GenVarItem[ind]];
	rr ← tr;
	cb[tr] ← cc;
        END;
      ENDCASE => ERROR;
    END;

  EasilyLoadable: PUBLIC PROCEDURE
      [var: VarComponent, dir: MoveDirection]
      RETURNS [evar: VarComponent] =
    BEGIN -- dir = store means it could be clobbered between loads
    size: CARDINAL = P5L.Words[var.wSize, var.bSize]; -- < 3
    IF P5L.EasyToLoad[var, dir] THEN RETURN[var];
    WITH vv: var SELECT FROM
      stack =>
        BEGIN
        loc: StackLocRec ← Stack.Loc[vv.sti, size];
        WITH loc SELECT FROM
          inTemp =>
            BEGIN
            tvar: VarComponent ← [wSize: vv.wSize, bSize: vv.bSize,
              space: frame[
                immutable: TRUE, level: tLevel, wd: tOffset, bd: vv.bd]];
            Stack.Forget[vv.sti, size];
            RETURN [EasilyLoadable[tvar, dir]];
            END;
          inLink =>
            BEGIN
            tvar: VarComponent ← [wSize: 1, space: link[wd: link]];
            Stack.Forget[vv.sti, size];
            RETURN [tvar];
            END;
          ENDCASE;
        END;
      ENDCASE;
    P5L.LoadComponent[var];
    RETURN[Stack.TempStore[size]];
    END;

  EasyToLoad: PUBLIC PROCEDURE
      [var: VarComponent, dir: MoveDirection]
      RETURNS [BOOLEAN] =
    BEGIN -- dir = store means it could be clobbered between loads
    lvl: ContextLevel;
    WITH vv: var SELECT FROM
      const, link, linkup, caddr, code => RETURN[TRUE];
      faddr => lvl ← vv.level;
      frame => 
        BEGIN
        IF vv.bd # 0
          OR var.bSize # 0
          OR var.wSize ~IN [1..2]
          OR (dir = store AND ~vv.immutable) THEN RETURN[FALSE];
        lvl ← vv.level;
        END;
      frameup => 
        BEGIN
        IF dir = store AND ~vv.immutable THEN RETURN[FALSE];
        lvl ← vv.level;
        END;
      ENDCASE => RETURN[FALSE];
    SELECT lvl FROM
      lZ => ERROR;
      lG, CPtr.curctxlvl => RETURN[TRUE];
      ENDCASE => RETURN[FALSE];
    END;

  FieldOfComponent: PUBLIC PROCEDURE [var: POINTER TO VarComponent,
      wd, bd, wSize, bSize: CARDINAL ← 0] =
    BEGIN
    ModComponent[var, wd, bd];
    IF wSize = 0 THEN WITH vv: var↑ SELECT FROM
      const =>
	BEGIN OPEN InlineDefs;
	Mask: ARRAY [0..15] OF CARDINAL =
	  [0B, 1B, 3B, 7B, 17B, 37B, 77B, 177B, 377B, 777B,
	    1777B, 3777B, 7777B, 17777B, 37777B, 77777B];
        vv.d1 ← BITAND[BITSHIFT[vv.d1, vv.bd+bSize-wordlength],
	  Mask[bSize]];
	wSize ← 1; bSize ← 0;
	END;
      ENDCASE;
    var.wSize ← wSize; var.bSize ← bSize;
    END;

  FieldOfComponentOnly: PUBLIC PROCEDURE [var: POINTER TO VarComponent,
      wd, bd, wSize, bSize: CARDINAL ← 0] =
    BEGIN
    WITH vv: var↑ SELECT FROM 
      stack =>
        BEGIN -- throw away anything above this new field
	b: CARDINAL ← vv.bd + bd;
        ws: CARDINAL ← P5L.Words[wSize, bSize];
	vv.wd ← vv.wd + wd + b/wordlength;
	vv.bd ← b MOD wordlength;
        Stack.KeepOnly[Stack.Above[vv.sti, vv.wd], ws];
        var.wSize ← wSize; var.bSize ← bSize;
        END;
      ENDCASE => FieldOfComponent[var, wd, bd, wSize, bSize];
    END;

  FieldOfVar: PUBLIC PROCEDURE [r: VarIndex,
      wd, bd, wSize, bSize: CARDINAL ← 0] =
    BEGIN
    ModField: PROCEDURE [var: POINTER TO VarComponent] =
      BEGIN -- had better not cause a compaction
      b: CARDINAL;
      WITH vv: var↑ SELECT FROM
	frame =>
	  BEGIN
	  IF vv.level # lZ THEN ERROR;
	  b ← vv.bd + bd;
	  vv.wd ← vv.wd + wd + b/wordlength;
	  vv.bd ← b MOD wordlength;
	  END;
	code =>
	  BEGIN
	  b ← vv.bd + bd;
	  vv.wd ← vv.wd + wd + b/wordlength;
	  vv.bd ← b MOD wordlength;
	  END;
	ENDCASE => ERROR;
      var.wSize ← wSize; var.bSize ← bSize;
      END;
    
    WITH cb[r] SELECT FROM
      o => FieldOfComponent[@var, wd, bd, wSize, bSize];
      bo => ModField[@offset];
      bdo => ModField[@offset];
      ind => ModField[@offset];
      ENDCASE;
    END;

  FieldOfVarOnly: PUBLIC PROCEDURE [r: VarIndex,
      wd, bd, wSize, bSize: CARDINAL ← 0] =
    BEGIN
    WITH cb[r] SELECT FROM
      o => FieldOfComponentOnly[@var, wd, bd, wSize, bSize];
      ENDCASE => FieldOfVar[r, wd, bd, wSize, bSize];
    END;


  varCount, varMax: CARDINAL ← 0;

  GenVarItem: PUBLIC PROCEDURE [tag: VarTag] RETURNS [r: VarIndex] =
    BEGIN -- returns the cb-relative index of a VarItem
    varCount ← varCount + 1;
    varMax ← MAX[varMax, varCount];
    r ← P5U.GetChunk[(SELECT tag FROM
      o => SIZE[o VarItem],
      bo => SIZE[bo VarItem],
      bdo => SIZE[bdo VarItem],
      ind => SIZE[ind VarItem],
      ENDCASE => ERROR)];
    RETURN
    END;

  InCode: PUBLIC PROCEDURE [r: VarIndex] RETURNS [BOOLEAN] =
    BEGIN
    WITH cb[r] SELECT FROM
      o => RETURN [var.tag = code];
      bo => RETURN [offset.tag = code];
      bdo => RETURN [offset.tag = code];
      ind => RETURN [offset.tag = code];
      ENDCASE => ERROR;
    END;

  LongVarAddress: PUBLIC PROCEDURE [r: VarIndex] RETURNS [BOOLEAN] =
    BEGIN
    WITH cb[r] SELECT FROM
      o => RETURN[FALSE];
      bo => RETURN [P5L.Words[base.wSize, base.bSize] > 1];
      bdo => RETURN [P5L.Words[disp.wSize, disp.bSize] > 1 OR 
	P5L.Words[base.wSize, base.bSize] > 1];
      ind => RETURN [P5L.Words[base.wSize, base.bSize] > 1];
      ENDCASE => ERROR;
    END;

  ModComponent: PUBLIC PROCEDURE [var: POINTER TO VarComponent,
      wd, bd: CARDINAL ← 0] =
    BEGIN
    b: CARDINAL;
    WITH vv: var↑ SELECT FROM 
      stack =>
        BEGIN
        nsti: StackIndex;
        b ← vv.bd + bd;
        nsti ← Stack.Above[vv.sti, wd + b/wordlength];
        vv.sti ← nsti; vv.bd ← b MOD wordlength;
        END;
      frame => 
        BEGIN
        b ← vv.bd + bd;
        vv.wd ← vv.wd + wd + b/wordlength;
        vv.bd ← b MOD wordlength;
        END;
      code => 
        BEGIN -- lets hear it for cross jumping
        b ← vv.bd + bd;
        vv.wd ← vv.wd + wd + b/wordlength;
        vv.bd ← b MOD wordlength;
        END;
      const =>
        BEGIN
        b ← vv.bd + bd;
        SELECT wd + b/wordlength FROM
          0 => NULL;
          1 => vv.d1 ← vv.d2;
          ENDCASE => ERROR;
        vv.bd ← b MOD wordlength;
        END;
      ENDCASE => ERROR;
    END;

  NormalizeExp: PUBLIC PROCEDURE [
      r: VarIndex, tempsei: ISEIndex ← ISENull, codeOk: BOOLEAN ← FALSE]
      RETURNS [nwords: CARDINAL, long: BOOLEAN, tsei: ISEIndex] =
    BEGIN
    wSize: CARDINAL;
    bSize: [0..wordlength);
    tsei ← tempsei;
    [wSize: wSize, bSize: bSize] ← P5L.VarAlignment[r, load];
    nwords ← P5L.Words[wSize, bSize];
    IF nwords <= 2 THEN BEGIN P5L.LoadVar[r]; long ← FALSE END
    ELSE IF codeOk OR ~P5L.InCode[r] THEN long ← P5L.LoadAddress[r, codeOk]
    ELSE
      BEGIN
      tvar: VarComponent;
      IF tsei = ISENull THEN tsei ← P5.GenAnonLex[nwords].lexsei;
      [var: tvar, sei: tsei] ← P5L.CopyToTemp[r, tsei];
      P5L.LoadComponent[P5L.AddrComponent[tvar]];
      long ← FALSE;
      END;
    END;

  NormalLex: PUBLIC PROCEDURE [nwords: CARDINAL, long, code: BOOLEAN ← FALSE]
      RETURNS [Lexeme] =
    BEGIN
    IF nwords <= 2 THEN RETURN[P5L.TOSLex[nwords]]
    ELSE IF code THEN RETURN[P5L.TOSCodeAddrLex[nwords]]
    ELSE RETURN[P5L.TOSAddrLex[nwords, long]];
    END;
    

  OVarItem: PUBLIC PROCEDURE [var: VarComponent] RETURNS [r: VarIndex] =
    BEGIN
    r ← P5L.GenVarItem[o];
    cb[r] ← [body: o[var: var]];
    END;

  ReleaseLex: PUBLIC PROCEDURE [lex: Lexeme] =
    BEGIN
    WITH lex SELECT FROM
      bdo => ReleaseVarItem[lexbdoi];
      ENDCASE;
    END;

  PFSize: CARDINAL = 4;
  pendingFree: ARRAY [0..PFSize) OF VarIndex ← [VarNull, VarNull, VarNull, VarNull];
  pfFirst, pfLast: CARDINAL ← 0;
  BadRelease: PUBLIC SIGNAL [badr: VarIndex] = CODE;

  ReleaseVarItem: PUBLIC PROCEDURE [r: VarIndex] =
    BEGIN
    i: CARDINAL;
    IF r = VarNull OR cb[r].free THEN GO TO bad;
    FOR i IN [0..PFSize) DO
      IF pendingFree[i] = r THEN GO TO bad;
      ENDLOOP;
    pfLast ← (pfLast+1) MOD PFSize;
    IF pfLast = pfFirst THEN
      BEGIN
      ReleaseReally[pendingFree[pfFirst]];
      pfFirst ← (pfFirst+1) MOD PFSize;
      END;
    pendingFree[pfLast] ← r;
    RETURN;
    EXITS
      bad => SIGNAL BadRelease[r];
    END;

  ReleaseReally: PROCEDURE [r: VarIndex] =
    BEGIN
    IF r = VarNull THEN RETURN;
    varCount ← varCount - 1;
    P5U.FreeChunk[r, (WITH cb[r] SELECT FROM
      o => SIZE[o VarItem],
      bo => SIZE[bo VarItem],
      bdo => SIZE[bdo VarItem],
      ind => SIZE[ind VarItem],
      ENDCASE => ERROR)];
    RETURN
    END;

  ReusableCopies: PUBLIC PROCEDURE [r: VarIndex, dir: MoveDirection, stackOk: BOOLEAN ← TRUE]
      RETURNS [r1, r2: VarIndex] =
    BEGIN -- make sure r has reusable pointer parts
    BEGIN -- to set up "doBo" exit
    WITH cc: cb[r] SELECT FROM
      o => IF ~stackOk THEN WITH cc.var SELECT FROM
	stack => 
	  BEGIN
	  tvar: VarComponent = CopyToTemp[r].var;
	  r1 ← OVarItem[tvar];
	  END;
	frameup => IF ~immutable THEN GO TO doBo;
	ENDCASE;
      ind =>
        IF cc.packtag = packed THEN
	  BEGIN
	  cc.base ← P5L.EasilyLoadable[cc.base, dir];
	  cc.index ← P5L.EasilyLoadable[cc.index, dir];
	  END
        ELSE GO TO doBo;
      ENDCASE => GO TO doBo;
    EXITS
      doBo =>
	BEGIN
	bor: BoVarIndex = P5L.MakeBo[r];
	cb[bor].base ← P5L.EasilyLoadable[cb[bor].base, dir];
	r1 ← bor;
	END;
    END;
    r2 ← P5L.CopyVarItem[r1];
    END;

  TOSAddrLex: PUBLIC PROCEDURE [size: CARDINAL, long: BOOLEAN ← FALSE] 
      RETURNS [bdo Lexeme] =
    BEGIN
    r: VarIndex = P5L.GenVarItem[bo];
    base: VarComponent = TOSComponent[IF long THEN 2 ELSE 1];
    IF size = 0 THEN ERROR;
    cb[r] ← [body: bo[base: base, offset: [wSize: size, space: frame[]]]];
    RETURN [[bdo[r]]];
    END;

  TOSCodeAddrLex: PUBLIC PROCEDURE [size: CARDINAL] RETURNS [bdo Lexeme] =
    BEGIN
    r: VarIndex = P5L.GenVarItem[bo];
    base: VarComponent = TOSComponent[1];
    IF size = 0 THEN ERROR;
    cb[r] ← [body: bo[base: base, offset: [wSize: size, space: code[]]]];
    RETURN [[bdo[r]]];
    END;

  TOSComponent: PUBLIC PROCEDURE [size: CARDINAL ← 1] RETURNS [VarComponent] =
    BEGIN
    IF size = 0 THEN ERROR;
    RETURN [[wSize: size, space: stack[sti: Stack.Top[size]]]];
    END;

  TOSLex: PUBLIC PROCEDURE [size: CARDINAL ← 1] RETURNS [Lexeme] =
    BEGIN
    r: VarIndex;
    SELECT size FROM
      0 => ERROR;
      1 => RETURN [[stack[Stack.Top[]]]];
      ENDCASE;
    r ← P5L.GenVarItem[o];
    cb[r] ← [body: o[var:
      [wSize: size, space: stack[sti: Stack.Top[size]]]]];
    RETURN [[bdo[r]]];
    END;
    
  VarAddressEasy: PUBLIC PROCEDURE [r: VarIndex] RETURNS [BOOLEAN] =
    BEGIN
    WITH cc: cb[r] SELECT FROM
      o => WITH vv: cc.var SELECT FROM
	code => RETURN [TRUE];
	linkup => RETURN [vv.delta = 0];
	frame => RETURN [vv.level = lG OR vv.level = CPtr.curctxlvl];
	frameup => RETURN [vv.delta = 0 AND
	  (vv.level = lG OR vv.level = CPtr.curctxlvl)];
	ENDCASE;
      bo => 
	WITH oo: cc.offset SELECT FROM
	  frame => IF oo.wd = 0 AND oo.level = lZ THEN
	    RETURN [P5L.EasyToLoad[cc.base, store]];
	  code => IF oo.wd = 0 THEN
	    RETURN [P5L.EasyToLoad[cc.base, store]];
	  ENDCASE;
      ENDCASE;
    RETURN[FALSE];
    END;

  VarAlignment: PUBLIC PROCEDURE [r: VarIndex, dir: MoveDirection]
      RETURNS [bd, bSize: [0..wordlength), wSize: CARDINAL] =
    BEGIN
    WITH cc: cb[r] SELECT FROM
      o =>
	BEGIN
	WITH vv: cc.var SELECT FROM
	  frame => bd ← vv.bd;
	  code => BEGIN IF dir = store THEN ERROR; bd ← vv.bd; END;
	  stack => BEGIN IF dir = store THEN ERROR; bd ← vv.bd; END;
	  const => BEGIN IF dir = store THEN ERROR; bd ← vv.bd; END;
	  ENDCASE =>  BEGIN IF dir = store THEN ERROR; bd ← 0; END;
	wSize ← cc.var.wSize;
	bSize ← cc.var.bSize;
	END; 
      bo =>
	BEGIN
	WITH oo: cc.offset SELECT FROM
	  frame => bd ← oo.bd;
	  code => BEGIN IF dir = store THEN ERROR; bd ← oo.bd; END;
	  ENDCASE => ERROR;
	wSize ← cc.offset.wSize;
	bSize ← cc.offset.bSize;
	END; 
      bdo =>
	BEGIN
	WITH oo: cc.offset SELECT FROM
	  frame => bd ← oo.bd;
	  code => BEGIN IF dir = store THEN ERROR; bd ← oo.bd; END;
	  ENDCASE => ERROR;
	wSize ← cc.offset.wSize;
	bSize ← cc.offset.bSize;
	END; 
      ind =>
	BEGIN
	WITH oo: cc.offset SELECT FROM
	  frame => bd ← oo.bd;
	  code => BEGIN IF dir = store THEN ERROR; bd ← oo.bd; END;
	  ENDCASE => ERROR;
	wSize ← cc.offset.wSize;
	bSize ← cc.offset.bSize;
	END; 
      ENDCASE => ERROR;
    END;

  VarFinal: PUBLIC PROCEDURE =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..PFSize) DO
      IF pendingFree[i] # VarNull THEN
	BEGIN
	ReleaseReally[pendingFree[i]];
	pendingFree[i] ← VarNull;
	END;
      ENDLOOP;
    END;

  VarForLex: PUBLIC PROCEDURE [l: Lexeme] RETURNS [r: VarIndex] = 
    BEGIN
    var: VarComponent;
    WITH ll: l SELECT FROM
      bdo => RETURN [ll.lexbdoi];
      ENDCASE => var ← ComponentForLex[l];
    r ← P5L.GenVarItem[o];
    cb[r] ← [body: o[var: var]];
    END;

  VarStackWords: PUBLIC PROCEDURE [r: VarIndex] RETURNS [nW: CARDINAL] =
    BEGIN -- number of words on the virtual stack
    nW ← 0;
    WITH cb[r] SELECT FROM
      o => WITH vv: var SELECT FROM
	stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	ENDCASE;
      bo => WITH vv: base SELECT FROM
	stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	ENDCASE;
      bdo => 
	BEGIN
	WITH vv: base SELECT FROM
	  stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	  ENDCASE;
	WITH vv: disp SELECT FROM
	  stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	  ENDCASE;
	END;
      ind => 
	BEGIN
	WITH vv: base SELECT FROM
	  stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	  ENDCASE;
	WITH vv: index SELECT FROM
	  stack => nW ← nW + P5L.Words[vv.wSize, vv.bSize];
	  ENDCASE;
	END;
      ENDCASE;
    RETURN
    END;
    

  END.