-- Constructor.mesa
-- last modified by Sweet, January 21, 1981  10:49 PM
-- last modified by Satterthwaite, May 5, 1983 11:12 am

DIRECTORY
  Alloc: TYPE USING [Notifier],
  Code: TYPE USING [CodeNotImplemented, curctxlvl],
  CodeDefs: TYPE USING [
    Base, BoVarIndex, Byte, codeType, ConsDestination, Lexeme, LabelCCIndex,
    MaxParmsInStack, NullLex, StoreOptions, VarComponent, VarIndex, VarNull],
  ComData: TYPE USING [switches, tC0],
  Counting: TYPE USING [Allocate, FillCounted, VarVarAssignCounted],
  Environment: TYPE USING [bitsPerCharacter, bitsPerWord],
  FOpCodes: TYPE USING [
    qADD, qALLOC, qBLT, qBLTL, qBLZL, qDSUB, qDUP, qGADRB, qLADRB, qLCO,
    qLI, qLP, qMUL, qOR, qPUSH, qSUB, qSHIFT, qWS, qWSD, qWSF],
  Inline: TYPE USING [BITOR, BITSHIFT],
  LiteralOps: TYPE USING [MasterString],
  Literals: TYPE USING [Base, MSTIndex, stType],
  P5: TYPE USING [
    ConstructOnStack, Exp, GenTempLex, MoveToCodeWord, MultiZero,
    P5Error, PushRhs, StoreMod, SysCall, WriteCodeWord, ZoneOp],
  P5L: TYPE USING [
    AdjustComponent, ComponentForLex, ComponentForSE, CopyToTemp,
    CopyVarItem, EasilyLoadable, FieldOfComponent, GenAdd, GenVarItem, 
    LoadAddress, LoadComponent, LoadVar, MakeBo, ModComponent, OVarItem,
    ReleaseVarItem, ReusableCopies, TOSAddrLex, TOSLex, VarForLex,
    VarVarAssign, Words],
  P5U: TYPE USING [
    ComputeFrameSize, InsertLabel, MakeTreeLiteral, NextVar, LabelAlloc, OperandType,
    Out0, Out1, Out2, OutJump, PushLitVal, RecordConstant, TreeLiteral, TreeLiteralValue,
    TypeForTree, WordAligned, WordsForOperand],
  PrincOps: TYPE USING [AllocationVectorSize, FieldDescriptor],
  SDDefs: TYPE USING [sStringInit],
  Stack: TYPE USING [
    Also, Decr, Dump, Forget, Incr, Mark, Pop, TempStore, Top],
  Symbols: TYPE USING [
    Base, ArraySEIndex, BitAddress, BitCount, ContextLevel, CSEIndex, CTXIndex,
    ISEIndex, ISENull, lG, lZ, RecordSEIndex, SEIndex, seType, typeANY],
  SymbolOps: TYPE USING [
    BitsPerElement, Cardinality, FirstCtxSe, FnField, NextSe, RCType, RecField,
    RecordRoot, UnderType, VariantField, WordsForType],
  Tree: TYPE USING [Base, Index, Link, Map, NodeName, Null, Scan, treeType],
  TreeOps: TYPE USING [
    FreeNode, GetNode, GetSe, NthSon, OpName, ScanList, UpdateList];

Constructor: PROGRAM
    IMPORTS 
      MPtr: ComData, CPtr: Code, Counting, Inline, LiteralOps,
      P5, P5L, P5U, Stack, SymbolOps, TreeOps
    EXPORTS CodeDefs, P5 =
  BEGIN
  OPEN CodeDefs, SymbolOps;

  -- imported definitions

  wordlength: CARDINAL = Environment.bitsPerWord;
  charlength: CARDINAL = Environment.bitsPerCharacter;

  ArraySEIndex: TYPE = Symbols.ArraySEIndex;
  BitAddress: TYPE = Symbols.BitAddress;
  BitCount: TYPE = Symbols.BitCount;
  ContextLevel: TYPE = Symbols.ContextLevel;
  CSEIndex: TYPE = Symbols.CSEIndex;
  CTXIndex: TYPE = Symbols.CTXIndex;
  ISEIndex: TYPE = Symbols.ISEIndex;
  ISENull: ISEIndex = Symbols.ISENull;
  lZ: ContextLevel = Symbols.lZ;
  lG: ContextLevel = Symbols.lG;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;
  SEIndex: TYPE = Symbols.SEIndex;
  typeANY: CSEIndex = Symbols.typeANY;	-- don't-care type for ConsAssign


  tb: Tree.Base;                -- tree base (local copy)
  seb: Symbols.Base;            -- semantic entry base (local copy)
  cb: CodeDefs.Base;            -- code base (local copy)
  stb: Literals.Base;           -- string base (local copy)

  ConstructorNotify: PUBLIC Alloc.Notifier =
    BEGIN  -- called by allocator whenever table area is repacked
    seb ← base[Symbols.seType];
    stb ← base[Literals.stType];
    tb ← base[Tree.treeType];
    cb ← base[codeType];
    END;

  -- state data and common code for construction

  OffsetRef: TYPE = POINTER TO READONLY VarComponent.frame;
  ConstructionError: SIGNAL = CODE;

  cd: PUBLIC ConsDestination;

  SetConsDest: PROC [r: VarIndex, exp: BOOL ← FALSE]
      RETURNS [rVal: VarIndex ← VarNull] =
    BEGIN
    bor: BoVarIndex;
    base: VarComponent;
    WITH cb[r] SELECT FROM
      o => WITH vv: var SELECT FROM
	frame =>
	  IF vv.level = lG OR vv.level = CPtr.curctxlvl THEN
	    BEGIN
	    cd.bd ← vv.bd;  cd.fOffset ← vv.wd;  cd.fLevel ← vv.level;
	    cd.inFrame ← TRUE;   cd.pLength ← 1;	-- in case loaded
	    cd.wSize ← vv.wSize;  cd.bSize ← vv.bSize;
	    IF exp THEN rVal ← r ELSE P5L.ReleaseVarItem[r];
	    RETURN
	    END;
	frameup, linkup => NULL;
	ENDCASE => ERROR;
      bo, bdo, ind => NULL;
      ENDCASE => ERROR;
    bor ← P5L.MakeBo[r];
    IF bor = VarNull THEN SIGNAL ConstructionError; -- should be caught above
    IF exp THEN 
      BEGIN
      r1: VarIndex;
      [first: r1, next: rVal] ← P5L.ReusableCopies[bor, store, FALSE];
      bor ← P5L.MakeBo[r1];
      END;
    BEGIN
    offset: VarComponent = cb[bor].offset;
    WITH vv: offset SELECT FROM
      frame =>
	BEGIN
	cd.bd ← vv.bd;
	cd.pDelta ← -INTEGER[vv.wd];
	cd.wSize ← vv.wSize;  cd.bSize ← vv.bSize;
	END;
      ENDCASE => ERROR;
    base ← cb[bor].base;
    P5L.ReleaseVarItem[bor]; -- we're finished with it now
    WITH vv: base SELECT FROM
      frame =>
	BEGIN
	IF vv.bSize # 0 OR ~(vv.level = lG OR vv.level = CPtr.curctxlvl) THEN
	  GO TO loadIt;
	cd.pLevel ← vv.level; cd.pOffset ← vv.wd;
	cd.pLength ← vv.wSize;
	END;
      link => {cd.pLink ← TRUE; cd.pOffset ← vv.wd};
      ENDCASE => GO TO loadIt;
    EXITS
      loadIt =>
	BEGIN
	wS: CARDINAL = P5L.Words[base.wSize, base.bSize];
	P5L.LoadComponent[base];
	cd.pSti ← Stack.Top[wS];
	Stack.Also[n: wS, inLink: FALSE, tLevel: lZ, tOffset: 0]; --forget
	cd.pLoaded ← TRUE;  cd.pLength ← wS;
	END;
    END;
    END;

  CountedAssign: PROC [type: CSEIndex, const: BOOL] RETURNS [BOOL] = INLINE
    BEGIN
    RETURN [cd.options.counted AND RCType[type]#none AND ~(const AND cd.options.init)]
    END;
    
 
  GetPointer: PROC [owd: CARDINAL]
       RETURNS [aVar: VarComponent, newOwd: CARDINAL] =
    BEGIN -- exit invariant: cd.pLoaded => newOwd + cd.pDelta = owd
    SELECT TRUE FROM
      cd.pLoaded => 
	IF cd.inFrame AND INTEGER[owd] < cd.pDelta AND (cd.fOffset+owd) IN Byte THEN
	  BEGIN
	  PopPointer[];
	  cd.pLink ← FALSE; cd.pLevel ← lZ;
	  cd.pDelta ← owd; newOwd ← 0;
	  aVar ← [wSize: 1, space: faddr[wd: cd.fOffset+owd, level: cd.fLevel]];
	  END
	ELSE aVar ← [wSize: cd.pLength, space: stack[sti: cd.pSti]];
      cd.inFrame => 
        BEGIN
        aVar ← [wSize: 1, space: faddr[wd: cd.fOffset+owd, level: cd.fLevel]];
        cd.pLength ← 1;  cd.pDelta ← owd; -- to be right if loaded
	newOwd ← 0;
	cd.pLevel ← lZ; --forget copy of old pointer in frame (can do better some day)
	RETURN
        END;
      cd.pLink => aVar ← [wSize: 1, space: link[wd: cd.pOffset]];
      cd.pLevel # lZ  =>
        aVar ← [wSize: cd.pLength, space:
          frame[wd: cd.pOffset, level: cd.pLevel, immutable: TRUE]];
      ENDCASE => ERROR;
    IF INTEGER[owd] IN [cd.pDelta .. cd.pDelta + Byte.LAST] THEN
      newOwd ← CARDINAL[INTEGER[owd] - cd.pDelta]
    ELSE
      BEGIN
      P5L.LoadComponent[aVar];
      IF INTEGER[owd] > cd.pDelta THEN
	P5L.GenAdd[INTEGER[owd] - cd.pDelta, cd.pLength # 1]
      ELSE
	BEGIN
	P5U.Out1[FOpCodes.qLI, CARDINAL[cd.pDelta] - owd];
	IF cd.pLength # 1 THEN
	  {P5U.Out1[FOpCodes.qLI, 0]; P5U.Out0[FOpCodes.qDSUB]}
	ELSE P5U.Out0[FOpCodes.qSUB];
	END;
      cd.pDelta ← owd;
      cd.pSti ← Stack.Top[cd.pLength];
      newOwd ← 0; cd.pLink ← FALSE; cd.pLevel ← lZ;
      aVar ← [wSize: cd.pLength, space: stack[sti: cd.pSti]];
      cd.pLoaded ← TRUE;
      END;
    END;
  
  LoadPointer: PROC [owd: CARDINAL] RETURNS [newOwd: CARDINAL] =
    BEGIN
    aVar: VarComponent;
    [aVar, newOwd] ← GetPointer[owd];
    P5L.LoadComponent[aVar];
    cd.pSti ← Stack.Top[cd.pLength];
    cd.pLoaded ← TRUE;
    END;
  
  DumpPointer: PROC =
    BEGIN
    IF cd.pLoaded THEN
      BEGIN
      IF cd.pLink OR cd.pLevel # lZ THEN PopPointer[]
      ELSE
	BEGIN
	aVar: VarComponent = Stack.TempStore[cd.pLength];
	WITH aVar SELECT FROM
	  frame => {cd.pLevel ← level; cd.pOffset ← wd};
	  link => {cd.pLink ← TRUE; cd.pOffset ← wd};
	  ENDCASE => ERROR;
	END;
      cd.pLoaded ← FALSE;
      END;
    END;

  PopPointer: PROC =
    BEGIN
    IF cd.pLoaded THEN Stack.Pop[cd.pLength];
    cd.pLoaded ← FALSE;
    END;
    
    
  ConsAssign: PROC [type: CSEIndex, atO: OffsetRef, t: Tree.Link, l: Lexeme ← NullLex] =
    BEGIN
    dest: VarIndex;
    source: VarIndex;
    offset: VarComponent.frame ← atO↑;
    counted: BOOL = CountedAssign[type, P5U.TreeLiteral[t]];
    useFrame: BOOL = cd.inFrame AND offset.wSize IN [1..2] AND
      offset.bSize = 0 AND (cd.fOffset+offset.wd) IN Byte;
    useSwapped: BOOL = ~useFrame AND cd.pLength = 1 AND ~counted AND
      (offset.wSize = 0 OR (offset.bSize = 0 AND offset.wSize IN [1..2]));
    offset.bd ← offset.bd + cd.bd;
    IF cd.pLoaded THEN
      SELECT TRUE FROM
	useFrame => PopPointer[];
	~useSwapped => DumpPointer[];
	ENDCASE;
    IF useSwapped THEN offset.wd ← LoadPointer[offset.wd];
    source ← P5L.VarForLex[IF l # NullLex
      THEN l
      ELSE P5.Exp[IF offset.wSize = 0 AND TreeOps.OpName[t] = mod
 	THEN P5.StoreMod[t, offset.bSize] ELSE t]];
    IF useSwapped THEN
      BEGIN
      base: VarComponent;
      P5L.LoadVar[source];
      WITH cb[cd.pSti] SELECT FROM
	onStack =>
	  BEGIN
	  WSOp: ARRAY [1..2] OF Byte = [FOpCodes.qWS, FOpCodes.qWSD];
	  IF offset.bSize = 0 THEN P5U.Out1[WSOp[offset.wSize], offset.wd]
	  ELSE P5U.Out2[FOpCodes.qWSF, offset.wd,
	    LOOPHOLE[PrincOps.FieldDescriptor[
	      offset: 0, posn: offset.bd, size: offset.bSize]]];
	  IF cd.remaining # 0 THEN
	    BEGIN
	    P5U.Out0[FOpCodes.qPUSH];
	    cd.pSti ← Stack.Top[];
	    Stack.Also[n: 1, inLink: cd.pLink, tOffset: cd.pOffset, tLevel: cd.pLevel];
	    END
	  ELSE cd.pLoaded ← FALSE;
	  RETURN
	  END;
	inTemp =>
	  BEGIN
	  cd.pLevel ← tLevel; cd.pOffset ← tOffset;
	  base ← [wSize: 1, space: frame[wd: tOffset, level: tLevel, immutable: TRUE]];
	  END;
	inLink =>
	  BEGIN
	  cd.pLink ← TRUE; cd.pOffset ← link;
	  base ← [wSize: 1, space: link[wd: link]];
	  END;
	ENDCASE => ERROR;
      -- would have used swap but pointer got dumped when evaluating field
      Stack.Forget[cd.pSti];
      cd.pLoaded ← FALSE;
      source ← P5L.VarForLex[P5L.TOSLex[P5L.Words[offset.wSize, offset.bSize]]];
      dest ← P5L.GenVarItem[bo];
      cb[dest] ← [body: bo[base: base, offset: offset]];
      END
    ELSE IF useFrame THEN
      BEGIN
      offset.wd ← offset.wd + cd.fOffset;
      offset.level ← cd.fLevel;
      dest ← P5L.OVarItem[offset];
      END
    ELSE
      BEGIN
      base: VarComponent;
      [base, offset.wd] ← GetPointer[offset.wd];
      IF cd.remaining # 0 THEN WITH base SELECT FROM
        stack => IF ~cd.inFrame THEN
          BEGIN -- this is our only copy, save it away
          base ← Stack.TempStore[cd.pLength];
          WITH base SELECT FROM
            frame => {cd.pLevel ← level; cd.pOffset ← wd};
            link => {cd.pLink ← TRUE; cd.pOffset ← wd};
            ENDCASE => ERROR;
          cd.pLoaded ← FALSE;
          END;
        ENDCASE;
      dest ← P5L.GenVarItem[bo];
      cb[dest] ← [body: bo[base: base, offset: offset]];
      END;
    IF counted THEN
      BEGIN
      subOptions: StoreOptions ← cd.options;
      subOptions.composite ← (RCType[type] = composite);
      [] ← Counting.VarVarAssignCounted[
	to: dest, from: source, options: subOptions, type: type];
      END
    ELSE [] ← P5L.VarVarAssign[to: dest, from: source, isexp: FALSE];
    cd.pLoaded ← FALSE;
    END;


  VanillaCons: PROC [t: Tree.Link] RETURNS [vanilla: BOOL ← TRUE] =
    BEGIN

    CheckItem: Tree.Scan =
      BEGIN
      SELECT TreeOps.OpName[t] FROM
	rowcons, construct, all, union => vanilla ← FALSE;
	cast, pad => CheckItem[TreeOps.NthSon[t, 1]];
	ENDCASE => NULL;
      END;

    TreeOps.ScanList[t, CheckItem];  RETURN
    END;

  CountDups: Tree.Map =
    BEGIN
    v ← t;	-- normal case (see safen)
    IF t # Tree.Null THEN WITH t SELECT FROM
      subtree =>
        BEGIN
        node: Tree.Index = index;
        SELECT tb[node].name FROM
          rowcons, construct =>
	    IF tb[node].name = rowcons AND tb[node].attr1 THEN
	      cd.remaining ← cd.remaining+1
	    ELSE tb[node].son[2] ← TreeOps.UpdateList[tb[node].son[2], CountDups];
	  all =>
            BEGIN
	    aSei: Symbols.ArraySEIndex = tb[node].info;
	    IF BitsPerElement[seb[aSei].componentType, seb[aSei].packed] < wordlength
	     AND tb[node].son[1] # Tree.Null THEN
	      cd.remaining ← cd.remaining+1
            ELSE tb[node].son[1] ← CountDups[tb[node].son[1]];
            END;
          union =>
            BEGIN
            IF tb[node].attr2 THEN cd.remaining ← cd.remaining+1;
            tb[node].son[2] ← TreeOps.UpdateList[tb[node].son[2], CountDups];
            END;
          cast, pad =>
            tb[node].son[1] ← CountDups[tb[node].son[1]];
	  safen => 
	    IF cd.ignoreSafen AND (cd.options.init OR ~tb[node].attr1) THEN
              BEGIN
              v ← CountDups[tb[node].son[1]];
              tb[node].son[1] ← Tree.Null;  TreeOps.FreeNode[node];
              END
            ELSE
              BEGIN
	      r: VarIndex = P5L.VarForLex[P5.Exp[tb[node].son[1]]];
	      sei: ISEIndex = P5L.CopyToTemp[r].sei;
	      seb[sei].idType ← tb[node].info;
	      v ← [symbol[sei]];
	      cd.remaining ← cd.remaining+1;
	      TreeOps.FreeNode[node];
              END;
          ENDCASE => cd.remaining ← cd.remaining+1;
        END;
      ENDCASE => cd.remaining ← cd.remaining+1;
    RETURN
    END;

  ConstructCountDown: PROC =
    BEGIN
    IF cd.remaining = 0 THEN SIGNAL ConstructionError;
    cd.remaining ← cd.remaining - 1;
    END;

 
  ConstantFill: PROC [type: CSEIndex, atO: OffsetRef, t: Tree.Link] =
    BEGIN
    ConstructCountDown[];
    SELECT TRUE FROM
      MPtr.switches['m] AND
       P5.MultiZero[t] AND cd.pLength = 2 AND ~CountedAssign[type, TRUE] =>
	FillZeros[atO, P5L.Words[atO.wSize, atO.bSize]];
      -- test for other special cases here
      ENDCASE => ConsAssign[type, atO, t];
    END;
    
  FillZeros: PROC [atO: OffsetRef, nwords: CARDINAL] =
    BEGIN
    base: VarComponent;
    offset: VarComponent.frame ← atO↑;
    dest: VarIndex;
    IF cd.remaining # 0 AND ~(cd.pLength = 2 AND offset.wd = 0) THEN
      DumpPointer[];
    [base, offset.wd] ← GetPointer[offset.wd];
    dest ← P5L.GenVarItem[bo];
    cb[dest] ← [body: bo[base: base, offset: offset]];
    IF ~P5L.LoadAddress[dest] THEN P5U.Out0[FOpCodes.qLP];
    P5U.PushLitVal[nwords];
    P5U.Out0[FOpCodes.qBLZL];
    IF cd.remaining # 0 AND (cd.pLength = 2 AND offset.wd = 0) THEN
      cd.pLoaded ← TRUE
    ELSE {Stack.Pop[2]; cd.pLoaded ← FALSE};
    END;
    
    
 -- main drivers

  MainConstruct: PROC [
	maint: Tree.Link,
	rSei: CSEIndex,
        fa: PROC [ISEIndex] RETURNS [BitAddress, CARDINAL],
	atO: OffsetRef,
        fieldSei: ISEIndex ← ISENull] =
    BEGIN -- workhorse subroutine for construction in memory
    tOffset: VarComponent.frame = atO↑;
    totalBits: CARDINAL = tOffset.wSize*wordlength + tOffset.bSize;
    rcSei: RecordSEIndex;

    AssignField: PROC [root: Tree.Link] =
      BEGIN
      offset: VarComponent.frame;
      rep: BitAddress;
      res: CARDINAL;
      fieldType: CSEIndex = UnderType[seb[fieldSei].idType];
      IF root # Tree.Null THEN
        BEGIN
        [rep, res] ← fa[fieldSei];
        offset ← tOffset;
        P5L.FieldOfComponent[var: @offset, wd: rep.wd, bd: rep.bd,
          wSize: res/wordlength, bSize: res MOD wordlength];
        IF fa # FnField AND totalBits <= wordlength THEN
          P5L.AdjustComponent[
            var: @offset, rSei: rcSei, fSei: fieldSei, tBits: totalBits];
        DO -- until we get to something interesting
          SELECT TreeOps.OpName[root] FROM
	    pad =>
	      BEGIN
	      root ← TreeOps.NthSon[root, 1];
	      offset.wSize ← P5U.WordsForOperand[root]; offset.bSize ← 0;
	      END;
	    cast => root ← TreeOps.NthSon[root, 1];
	    ENDCASE => EXIT;
          ENDLOOP;
        SELECT TreeOps.OpName[root] FROM
          construct =>
	    MainConstruct[TreeOps.NthSon[root, 2], P5U.OperandType[root], RecField, @offset];
          union => UnionConstruct[TreeOps.GetNode[root], rcSei, atO];
          rowcons => Row[TreeOps.GetNode[root], @offset];
          all => [] ← AllConstruct[TreeOps.GetNode[root], @offset];
          mwconst => ConstantFill[fieldType, @offset, root];
          ENDCASE => {ConstructCountDown[]; ConsAssign[fieldType, @offset, root]};
	END; -- IF root # Tree.Null
      fieldSei ← P5U.NextVar[NextSe[fieldSei]];
      END; -- of AssignField

    IF fieldSei = ISENull THEN
      WITH seb[rSei] SELECT FROM
        record =>
          BEGIN
          rcSei ← RecordRoot[LOOPHOLE[rSei]];
          fieldSei ← P5U.NextVar[FirstCtxSe[seb[rcSei].fieldCtx]];
          END;
        ENDCASE => P5.P5Error[589]
    ELSE rcSei ← LOOPHOLE[rSei];
    TreeOps.ScanList[maint, AssignField];
    END; -- of MainConstruct


  Row: PROC [node: Tree.Index, atO: OffsetRef] =
    BEGIN  -- handles ARRAY construction
    aSei: ArraySEIndex = tb[node].info;
    IF seb[aSei].typeTag # array THEN P5.P5Error[580];
    IF tb[node].attr1 THEN
      BEGIN	-- row of string literals
      c: CARDINAL = P5.MoveToCodeWord[];
      n: CARDINAL ← 0;
      localText, globalText: BOOL ← FALSE;

      TextItem: PROC [t: Tree.Link] =
        BEGIN
        WITH e:t SELECT FROM
          literal =>
            WITH e.index SELECT FROM
              string =>
                BEGIN
                msti: Literals.MSTIndex = LiteralOps.MasterString[sti];
                IF stb[msti].local THEN localText ← TRUE ELSE globalText ← TRUE;
                P5.WriteCodeWord[stb[msti].info];
                END;
              ENDCASE => P5.P5Error[577];
          ENDCASE => P5.P5Error[578];
        n ← n+1;
        END;

      TreeOps.ScanList[tb[node].son[2], TextItem];
      ConstructCountDown[];
      Stack.Dump[]; Stack.Mark[];
      P5U.Out1[FOpCodes.qLCO, c];  P5U.PushLitVal[n];  P5U.RecordConstant[c, n];
      IF localText AND globalText OR cd.remaining # 0 AND ~cd.inFrame THEN
	SIGNAL CPtr.CodeNotImplemented;
      P5U.Out1[IF localText THEN FOpCodes.qLADRB ELSE FOpCodes.qGADRB, 0];
      [] ← LoadPointer[atO.wd];
      IF cd.pLength # 1 THEN -- does a long pointer to array of short strings
        SIGNAL CPtr.CodeNotImplemented; -- make any sense?
      P5.SysCall[SDDefs.sStringInit];
      cd.pLoaded ← FALSE;
      END
    ELSE
      BEGIN  -- not all string literals
      offset: VarComponent.frame ← atO↑;
      eWSize: CARDINAL;
      eBSize: [0..wordlength);
      cSei: CSEIndex = UnderType[seb[aSei].componentType];

      AssignElement: PROC [t: Tree.Link] =
	BEGIN
	DO -- until we get to something interesting
	  SELECT TreeOps.OpName[t] FROM
	    pad =>
	      BEGIN
	      t ← TreeOps.NthSon[t, 1];
	      offset.wSize ← P5U.WordsForOperand[t]; offset.bSize ← 0;
	      END;
	    cast => t ← TreeOps.NthSon[t, 1];
	    ENDCASE => EXIT;
          ENDLOOP;
	SELECT TreeOps.OpName[t] FROM
          rowcons => Row[TreeOps.GetNode[t], @offset];
          construct =>
	    MainConstruct[TreeOps.NthSon[t, 2], P5U.OperandType[t], RecField, @offset];
          all => -- convert this later
	    [] ← AllConstruct[TreeOps.GetNode[t], @offset];
          mwconst => ConstantFill[cSei, @offset, t];
          ENDCASE =>
	    IF t # Tree.Null THEN {ConstructCountDown[]; ConsAssign[cSei, @offset, t]};
	P5L.ModComponent[var: @offset, wd: eWSize, bd: offset.bSize];
	offset.wSize ← eWSize;  offset.bSize ← eBSize;
	END; -- of AssignElement

      totalBits: BitCount = atO.wSize.LONG*wordlength + atO.bSize;
      grain: BitCount = BitsPerElement[seb[aSei].componentType, seb[aSei].packed];
      packed: BOOL;
      fillBits: CARDINAL;
      IF grain >= wordlength THEN
	BEGIN
	packed ← FALSE;  fillBits ← 0;
	eBSize ← 0; eWSize ← WordsForType[seb[aSei].componentType];
	END
      ELSE
	BEGIN
	packed ← TRUE;
	fillBits ← totalBits - Cardinality[UnderType[seb[aSei].indexType]]*grain;
	IF fillBits # 0 AND totalBits > wordlength THEN 
	  cd.remaining ← cd.remaining+1;
	eWSize ← 0; eBSize ← grain;
	END;
      IF fillBits # 0 AND totalBits <= wordlength THEN 
	BEGIN
	bs: CARDINAL = eBSize + fillBits;
	offset.wSize ← bs / wordlength;
	offset.bSize ← bs MOD wordlength;
	fillBits ← 0;
	END
      ELSE {offset.wSize ← eWSize; offset.bSize ← eBSize};
      TreeOps.ScanList[tb[node].son[2], AssignElement];
      IF fillBits # 0 THEN
	BEGIN
	offset.wSize ← 0;  offset.bSize ← fillBits;
	ConstructCountDown[];  ConsAssign[typeANY, @offset, MPtr.tC0];
	END;
      END;
    END;


  UnionConstruct: PROC [node: Tree.Index, rootSei: RecordSEIndex, atO: OffsetRef] =
    BEGIN -- construct a union part, atO↑ is offset of beginning of record
    tOffset: VarComponent.frame = atO↑;
    offset: VarComponent.frame ← atO↑;
    fieldSei: ISEIndex;
    vCtx: CTXIndex;
    uSei: CSEIndex = tb[node].info;
    rcSei: RecordSEIndex;
    tSei: ISEIndex;
    tagged: BOOL;
    tagValue: CARDINAL;
    tBits: CARDINAL = tOffset.wSize*wordlength + tOffset.bSize;
    WITH u: seb[uSei] SELECT FROM
      union =>
	BEGIN
	tagged ← u.controlled;
	IF tagged THEN
          BEGIN
	  tagAddr: BitAddress = seb[u.tagSei].idValue;
	  tagSize: [0..wordlength] = seb[u.tagSei].idInfo;
	  P5L.FieldOfComponent[
	    var: @offset, wd: tagAddr.wd, bd: tagAddr.bd,
	    wSize: tagSize/wordlength, bSize: tagSize MOD wordlength];
          IF tBits <= wordlength THEN
            P5L.AdjustComponent[var: @offset, rSei: rootSei, fSei: u.tagSei, tBits: tBits];
	  END;
	END;
      ENDCASE => ERROR;
    tSei ← TreeOps.GetSe[tb[node].son[1]];
    tagValue ← seb[tSei].idValue;
    rcSei ← LOOPHOLE[UnderType[tSei], RecordSEIndex];
    vCtx ← seb[rcSei].fieldCtx;
    fieldSei ← P5U.NextVar[FirstCtxSe[vCtx]];
    IF tagged THEN
      BEGIN
      IF fieldSei # ISENull AND seb[fieldSei].idCtx # vCtx THEN
        BEGIN -- a dummy fill field
        fillSize: [0..wordlength) = seb[fieldSei].idInfo;
	b: CARDINAL = offset.bSize + fillSize;
        tagValue ← Inline.BITSHIFT[tagValue, fillSize];
        offset.bSize ← b MOD wordlength;  offset.wSize ← b/wordlength;
        fieldSei ← P5U.NextVar[NextSe[fieldSei]];
        END;
      ConstructCountDown[];
      ConsAssign[typeANY, @offset, P5U.MakeTreeLiteral[tagValue]];
      END
    ELSE IF fieldSei # ISENull AND seb[fieldSei].idCtx # vCtx THEN
      BEGIN -- no tag, but a fill field anyway
      fillSize: [0..wordlength) = seb[fieldSei].idInfo;
      fillAddr: BitAddress = seb[fieldSei].idValue; -- can't be full word
      P5L.FieldOfComponent[
	var: @offset, wd: fillAddr.wd, bd: fillAddr.bd, bSize: fillSize];
      IF tBits <= wordlength THEN
	P5L.AdjustComponent[var: @offset, rSei: rootSei, fSei: fieldSei, tBits: tBits];
      ConsAssign[typeANY, @offset, MPtr.tC0];
      fieldSei ← P5U.NextVar[NextSe[fieldSei]];
      END;
    IF fieldSei # ISENull THEN MainConstruct[tb[node].son[2], rootSei, RecField, atO, fieldSei];
    END;


  AllConstruct: PROC [node: Tree.Index, atO: OffsetRef, replCount: CARDINAL ← 1] 
      RETURNS [wordsFilled: CARDINAL] =
    BEGIN	-- reexamine when packed arrays of packed arrays
    aSei: ArraySEIndex = tb[node].info;
    tOffset: VarComponent.frame = atO↑;
    offset: VarComponent.frame ← atO↑;
    csei: CSEIndex = UnderType[seb[aSei].componentType];
    eWSize: CARDINAL;
    eBSize: [0..wordlength);
    t1: Tree.Link ← tb[node].son[1];
    totalBits: BitCount = tOffset.wSize.LONG*wordlength + tOffset.bSize;
    grain: BitCount = BitsPerElement[seb[aSei].componentType, seb[aSei].packed];
    packed: BOOL;
    fillBits, eCount: CARDINAL;
    e2Offset: CARDINAL;
    wordsFilled ← P5L.Words[tOffset.wSize, tOffset.bSize];
    IF grain >= wordlength THEN
      BEGIN
      packed ← FALSE;  fillBits ← 0;
      eBSize ← 0; eWSize ← WordsForType[seb[aSei].componentType];
      END
    ELSE
      BEGIN
      packed ← TRUE;
      eCount ← Cardinality[UnderType[seb[aSei].indexType]];
      fillBits ← totalBits - eCount*CARDINAL[grain];
      eWSize ← 0; eBSize ← grain;
      END;
    P5L.FieldOfComponent[var: @offset, wSize: eWSize, bSize: eBSize];
    IF fillBits # 0 AND totalBits <= wordlength THEN 
      BEGIN
      bs: CARDINAL = eBSize + fillBits;
      offset.wSize ← bs / wordlength;
      offset.bSize ← bs MOD wordlength;
      fillBits ← 0;
      END;
    IF fillBits = 0 THEN wordsFilled ← wordsFilled * replCount;
    IF t1 = Tree.Null THEN RETURN;
    DO -- until we get to something interesting
      SELECT TreeOps.OpName[t1] FROM
	pad =>
	  BEGIN
	  t1 ← TreeOps.NthSon[t1, 1];
	  offset.wSize ← P5U.WordsForOperand[t1]; offset.bSize ← 0;
	  END;
	cast => t1 ← TreeOps.NthSon[t1, 1];
	ENDCASE => EXIT;
      ENDLOOP;
    IF MPtr.switches['m] AND (P5.MultiZero[t1, 1] AND wordsFilled > 1)
     AND ~CountedAssign[csei, TRUE] THEN
      BEGIN
      ConstructCountDown[];
      IF fillBits # 0 THEN wordsFilled ← wordsFilled * replCount;
      FillZeros[atO, wordsFilled];
      e2Offset ← wordsFilled;  fillBits ← 0;
      END
    ELSE IF packed THEN
      BEGIN
      ePerWord: CARDINAL = wordlength/eBSize;
      fold: BOOL = P5U.TreeLiteral[t1];
      v: WORD;
      IF fillBits # 0 THEN cd.remaining ← cd.remaining+1;
      IF cd.pLoaded THEN
        SELECT TRUE FROM
	  (cd.pLength > 1) => DumpPointer[];
	  cd.inFrame => PopPointer[];
	  ENDCASE;
      SELECT TRUE FROM
	fold => v ← P5U.TreeLiteralValue[t1];
	(eBSize = 1) => {v ← 1; P5.PushRhs[t1]};
	ENDCASE => P5.PushRhs[t1];
      THROUGH (0..MIN[ePerWord, eCount]) DO
        IF fold OR eBSize = 1 THEN v ← Inline.BITOR[Inline.BITSHIFT[v, eBSize], v]
        ELSE
	  BEGIN
	  P5U.Out0[FOpCodes.qDUP];
	  P5U.PushLitVal[eBSize];  P5U.Out0[FOpCodes.qSHIFT];
	  P5U.Out0[FOpCodes.qOR];
	  END;
     	ENDLOOP;
      SELECT TRUE FROM
	fold => P5U.PushLitVal[v];
	(eBSize = 1) =>
	  BEGIN
	  tlabel: LabelCCIndex = P5U.LabelAlloc[];
	  elabel: LabelCCIndex = P5U.LabelAlloc[];
	  P5U.PushLitVal[0];  P5U.OutJump[JumpE, tlabel];
	  P5U.PushLitVal[v];  P5U.OutJump[Jump, elabel];
	  P5U.InsertLabel[tlabel];
	  Stack.Decr[1];  P5U.Out0[FOpCodes.qPUSH];
	  P5U.InsertLabel[elabel];
	  END;
	ENDCASE => NULL;
      IF totalBits < wordlength THEN P5L.FieldOfComponent[var: @offset, bSize: totalBits]
      ELSE P5L.FieldOfComponent[var: @offset, wSize: 1];
      ConsAssign[typeANY, @offset, Tree.Null, P5L.TOSLex[1]];
      P5L.ModComponent[var: @offset,
	bd: IF eCount > ePerWord THEN wordlength ELSE offset.bSize];
      IF wordsFilled <= 1 THEN -- all in one word case
	BEGIN
	ConstructCountDown[];
	IF cd.remaining = 0 AND cd.pLoaded THEN PopPointer[];
	END;
      e2Offset ← 1;
      END
    ELSE IF TreeOps.OpName[t1] = all THEN
      BEGIN  -- ~packed, set all elements in recursive call
      eCount ← Cardinality[UnderType[seb[aSei].indexType]];
      e2Offset ← AllConstruct[TreeOps.GetNode[t1], @offset, replCount*eCount];
      -- wordsFilled > e2Offset => cd.remaining has been incremented
      END
    ELSE
      BEGIN -- ~packed
      IF wordsFilled > eWSize THEN
	cd.remaining ← cd.remaining + 1; -- so only pointer isn't lost
      SELECT TreeOps.OpName[t1] FROM -- set first element
	construct =>
	  MainConstruct[TreeOps.NthSon[t1, 2], P5U.OperandType[t1], RecField, @offset];
	rowcons => Row[TreeOps.GetNode[t1], @offset];
	ENDCASE => {ConstructCountDown[]; ConsAssign[csei, @offset, t1]};
      e2Offset ← eWSize;
      END;
    IF wordsFilled > e2Offset THEN
      BEGIN
      bWords: CARDINAL = wordsFilled - e2Offset;
      IF ~CountedAssign[csei, P5U.TreeLiteral[t1]] THEN
	BEGIN
	BltOp: ARRAY [1..2] OF Byte = [FOpCodes.qBLT, FOpCodes.qBLTL];
	owd: CARDINAL ← LoadPointer[tOffset.wd]; -- load address of first element
	IF ~(cd.inFrame OR cd.pLink OR cd.pLevel # lZ) THEN
	  BEGIN -- we need to load at least twice, save in temp
	  tvar: VarComponent = Stack.TempStore[cd.pLength];
	  P5L.LoadComponent[tvar]; -- load it back
	  WITH vv: tvar SELECT FROM
	    frame => {cd.pLevel ← vv.level; cd.pOffset ← vv.wd};
	    ENDCASE => ERROR;
	  END;
	cd.pLoaded ← FALSE; -- cd.pSti was maybe invalid anyway
	IF owd # 0 THEN P5L.GenAdd[owd, cd.pLength # 1];
	ConstructCountDown[];
	P5U.Out1[FOpCodes.qLI, bWords];
	owd ← LoadPointer[tOffset.wd + e2Offset]; -- load address of second
	IF (cd.remaining # 0 AND ~(cd.inFrame OR cd.pLink OR cd.pLevel # lZ)) THEN
	  BEGIN -- still needed, save in temp
	  tvar: VarComponent = Stack.TempStore[cd.pLength];
	  P5L.LoadComponent[tvar]; -- load it back
	  WITH vv: tvar SELECT FROM
	    frame => {cd.pLevel ← vv.level; cd.pOffset ← vv.wd};
	    ENDCASE => ERROR;
	  END;
	cd.pLoaded ← FALSE; -- cd.pSti was maybe invalid anyway
	IF owd # 0 THEN P5L.GenAdd[owd, cd.pLength # 1];
	P5U.Out0[BltOp[cd.pLength]];
	END
      ELSE
	BEGIN
	aVar: VarComponent;
	r: VarIndex = P5L.GenVarItem[bo];
	rr: VarIndex = P5L.GenVarItem[bo];
	offset ← tOffset;  offset.wSize ← e2Offset;  offset.bSize ← 0;
	[aVar, offset.wd] ← GetPointer[offset.wd];
	IF cd.pLoaded THEN
	  BEGIN
	  aVar ← Stack.TempStore[cd.pLength];
	  IF cd.remaining # 0 AND ~cd.inFrame THEN
	    WITH aVar SELECT FROM
	      frame => {cd.pLevel ← level; cd.pOffset ← wd};
	      link => {cd.pLink ← TRUE; cd.pOffset ← wd};
	      ENDCASE => ERROR;
	  cd.pLoaded ← FALSE;
	  END;
	ConstructCountDown[];
	cb[r] ← [body: bo[base: aVar, offset: offset]];
	P5L.ModComponent[@offset, e2Offset];  offset.wSize ← bWords;
	cb[rr] ← [body: bo[base: aVar, offset: offset]];
	Counting.FillCounted[source: r, space: rr, type: csei, options: cd.options];
	END;
      IF fillBits # 0 THEN
	BEGIN
	usedBits: CARDINAL = eCount*CARDINAL[grain];
	offset ← tOffset;
	P5L.FieldOfComponent[var: @offset, wd: usedBits/wordlength,
	  bd: usedBits MOD wordlength, bSize: fillBits];
	IF replCount > 1 THEN cd.remaining ← cd.remaining + 1;  -- caller replicates
	ConstructCountDown[];  ConsAssign[typeANY, @offset, MPtr.tC0];
	END;
      END;
    RETURN
    END;


 -- public entries

  All: PUBLIC PROC [t: Tree.Link, node: Tree.Index, options: StoreOptions] RETURNS [Lexeme] =
    BEGIN
    r, rr: VarIndex;
    saveCd: ConsDestination = cd;
    offset: VarComponent.frame;
    aSei: Symbols.ArraySEIndex = tb[node].info;
    aWords: CARDINAL = WordsForType[aSei];
    cd ← [options: options, ignoreSafen: t = Tree.Null OR t.tag = symbol]; -- + defaults
    IF BitsPerElement[seb[aSei].componentType, seb[aSei].packed] < wordlength THEN
      cd.remaining ← 1
    ELSE tb[node].son[1] ← CountDups[tb[node].son[1]];
    r ← P5L.VarForLex[IF t = Tree.Null THEN P5.GenTempLex[aWords] ELSE P5.Exp[t]];
    rr ← SetConsDest[r, options.expr];
    offset ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
    [] ← AllConstruct[node, @offset];
    IF cd.remaining # 0 THEN SIGNAL ConstructionError;
    cd ← saveCd;
    RETURN [[bdo[rr]]]
    END;


  Construct: PUBLIC PROC [t: Tree.Link, node: Tree.Index, options: StoreOptions]
      RETURNS [Lexeme] =
    BEGIN  -- generate code for constructor expression
    tsei: RecordSEIndex = tb[node].info;
    nwords: CARDINAL = WordsForType[tsei];
    maxStackWords: NAT = (IF options.expr THEN MaxParmsInStack ELSE 2);
    r, rr: VarIndex;
    saveCd: ConsDestination = cd;
    offset: VarComponent.frame;
    packedDest: VarIndex ← VarNull;
    IF (~options.expr OR t = Tree.Null) AND nwords <= maxStackWords AND
        P5U.WordAligned[tsei] AND VanillaCons[tb[node].son[2]] THEN
      BEGIN -- can build in stack
      P5.ConstructOnStack[tb[node].son[2], tsei];
      IF ~options.expr THEN
        BEGIN
        rr ← P5L.VarForLex[P5L.TOSLex[nwords]];
        RETURN [P5L.VarVarAssign[P5L.VarForLex[P5.Exp[t]], rr, FALSE]]
        END
      ELSE RETURN [P5L.TOSLex[nwords]]
      END;
    cd ← [options: options, ignoreSafen: t = Tree.Null OR t.tag = symbol]; -- + many defaults
    tb[node].son[2] ← TreeOps.UpdateList[tb[node].son[2], CountDups];
    IF cd.remaining # 0 OR options.expr THEN
      BEGIN
      r ← P5L.VarForLex[IF t = Tree.Null THEN P5.GenTempLex[nwords] ELSE P5.Exp[t]];
      WITH cc: cb[r] SELECT FROM
	ind => WITH cc SELECT FROM
	  packed =>
	    BEGIN
	    var: VarComponent = P5L.ComponentForLex[P5.GenTempLex[1]];
	    packedDest ← r;  r ← P5L.OVarItem[var];
	    END;
	  ENDCASE;
	ENDCASE;
      rr ← SetConsDest[r, options.expr OR packedDest # VarNull];
      offset ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
      MainConstruct[
	tb[node].son[2], tsei, IF seb[tsei].argument THEN FnField ELSE RecField, @offset];
      END;
    IF cd.remaining # 0 THEN SIGNAL ConstructionError;
    IF packedDest # VarNull THEN	-- not RC
      IF options.expr THEN [] ← P5L.VarVarAssign[packedDest, P5L.CopyVarItem[rr], FALSE]
      ELSE BEGIN [] ← P5L.VarVarAssign[packedDest, rr, FALSE]; rr ← VarNull END;
    cd ← saveCd;
    RETURN [[bdo[rr]]]
    END;


  RowCons: PUBLIC PROC [t: Tree.Link, node: Tree.Index, options: StoreOptions]
      RETURNS [Lexeme] =
    BEGIN -- array (expression) construction
    r, rr: VarIndex;
    aSei: ArraySEIndex = tb[node].info;
    saveCd: ConsDestination = cd;
    offset: VarComponent.frame;
    cd ← [options: options, ignoreSafen: t = Tree.Null OR t.tag = symbol]; -- + many defaults
    IF tb[node].attr1 THEN cd.remaining ← 1
    ELSE tb[node].son[2] ← TreeOps.UpdateList[tb[node].son[2], CountDups];
    r ← P5L.VarForLex[IF t = Tree.Null THEN P5.GenTempLex[WordsForType[aSei]]
	ELSE P5.Exp[t]];
    rr ← SetConsDest[r, options.expr];
    offset ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
    Row[node, @offset];
    IF cd.remaining # 0 THEN SIGNAL ConstructionError;
    cd ← saveCd;
    RETURN [[bdo[rr]]]
    END;


  TransferConstruct: PUBLIC PROC [
      nparms: CARDINAL, resident: BOOL, t: Tree.Link, tsei: CSEIndex] =
    BEGIN -- generate code for construct statement
    lex: Lexeme;
    saveCd: ConsDestination = cd;
    offset: VarComponent.frame;
    fs: CARDINAL ← P5U.ComputeFrameSize[nparms];
    cd ← [ignoreSafen: FALSE]; -- + many defaults
    IF TreeOps.OpName[t] = safen THEN
      BEGIN  -- CountDups would free t
      node: Tree.Index = TreeOps.GetNode[t];
      r: VarIndex = P5L.VarForLex[P5.Exp[tb[node].son[1]]];
      sei: ISEIndex = P5L.CopyToTemp[r].sei;
      seb[sei].idType ← tb[node].info;
      t ← [symbol[sei]];
      END;
    t ← TreeOps.UpdateList[t, CountDups];
    IF resident THEN fs ← fs + PrincOps.AllocationVectorSize;
    P5U.PushLitVal[fs];  P5U.Out0[FOpCodes.qALLOC];
    cd.remaining ← cd.remaining + 1;
    IF cd.remaining # 1 THEN
      BEGIN
      lex ← P5L.TOSAddrLex[nparms];
      [] ← SetConsDest[P5L.VarForLex[lex], FALSE];
      offset ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
      MainConstruct[t, tsei, FnField, @offset];
      [] ← LoadPointer[0];
      END;
    IF cd.remaining # 1 THEN SIGNAL ConstructionError;
    cd ← saveCd;
    END;


  VariantConstruct: PUBLIC PROC [t1, t2: Tree.Link, options: StoreOptions] =
    BEGIN
    r: VarIndex;
    saveCd: ConsDestination = cd;
    offset: VarComponent.frame;
    rootSei: RecordSEIndex;
    t1 ← TreeOps.NthSon[t1, 1];
    cd ← [options: options, ignoreSafen: t1.tag = symbol]; -- + many defaults
    t2 ← TreeOps.UpdateList[t2, CountDups];
    IF cd.remaining # 0 THEN
      BEGIN
      rootSei ← RecordRoot[LOOPHOLE[P5U.OperandType[t1]]];
      r ← P5L.VarForLex[P5.Exp[t1]];
      WITH cc: cb[r] SELECT FROM
	ind => WITH cc SELECT FROM
	  packed => SIGNAL CPtr.CodeNotImplemented;
	  ENDCASE;
	ENDCASE;
      [] ← SetConsDest[r, FALSE];
      offset ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
      UnionConstruct[TreeOps.GetNode[t2], rootSei, @offset];
      END;
    IF cd.remaining # 0 THEN SIGNAL ConstructionError;
    cd ← saveCd;
    END;


  New: PUBLIC PROC [node: Tree.Index] RETURNS [Lexeme] =
    BEGIN
    long: BOOL = tb[node].attr2;
    counted: BOOL = tb[node].attr3;
    pLength: CARDINAL = WordsForType[tb[node].info];
    typeTree: Tree.Link = tb[node].son[2];
    overType: SEIndex = P5U.TypeForTree[typeTree];
    type: CSEIndex = UnderType[overType];
    catchTree: Tree.Link = IF tb[node].nSons = 4 THEN tb[node].son[4] ELSE Tree.Null;
    tag: ISEIndex ← ISENull;
    seqLength: VarComponent;
    computedType: BOOL = (TreeOps.OpName[typeTree] = apply);
    sizePusher: PROC = IF computedType THEN PushNewSize ELSE NIL;

    PushNewSize: PROC =
      BEGIN
      nw: CARDINAL = WordsForType[type];
      IF computedType THEN
	BEGIN
	subNode: Tree.Index = TreeOps.GetNode[typeTree];
	vSei: ISEIndex = VariantField[type];
	bitsPerItem, n: CARDINAL;
	IF vSei # ISENull THEN
	  BEGIN
	  vType: CSEIndex = UnderType[seb[vSei].idType];
	  WITH v: seb[vType] SELECT FROM
	    sequence =>
	      BEGIN
	      tag ← IF v.controlled THEN v.tagSei ELSE ISENull;
	      bitsPerItem ← BitsPerElement[v.componentType, v.packed];
	      END;
	    ENDCASE => ERROR;
	  END
	ELSE
	  BEGIN  -- must be StringBody, fudge it
	  tag ← NextSe[FirstCtxSe[seb[LOOPHOLE[type, RecordSEIndex]].fieldCtx]];
	  bitsPerItem ← charlength;
	  END;
	seqLength ← P5L.ComponentForLex[P5.Exp[tb[subNode].son[2]]];
	IF tag # ISENull THEN seqLength ← P5L.EasilyLoadable[seqLength, load];
	IF bitsPerItem >= wordlength THEN
	  BEGIN
	  n ← bitsPerItem/wordlength;
	  WITH s: seqLength SELECT FROM
	    const => P5U.PushLitVal[nw + n*s.d1];
	    ENDCASE =>
	      BEGIN
	      P5L.LoadComponent[seqLength];
	      IF n # 1 THEN {P5U.PushLitVal[n];  P5U.Out0[FOpCodes.qMUL]};
	      P5U.PushLitVal[nw];  P5U.Out0[FOpCodes.qADD];
	      END;
	  END
	ELSE
	  BEGIN
	  n ← wordlength/bitsPerItem;
	  WITH s: seqLength SELECT FROM
	    const => P5U.PushLitVal[nw + ((s.d1+(n-1))/n)];
	    ENDCASE =>
	      BEGIN
	      P5L.LoadComponent[seqLength];
	      P5U.PushLitVal[n-1];  P5U.Out0[FOpCodes.qADD];
	      P5U.PushLitVal[SELECT n FROM 2 => -1, 4 => -2, 8 => -3, ENDCASE => -4];
	      P5U.Out0[FOpCodes.qSHIFT];
	      P5U.PushLitVal[nw];  P5U.Out0[FOpCodes.qADD];
	      END;
	  END;
	END
      ELSE P5U.PushLitVal[nw];
      END;

    zoneTree: Tree.Link = tb[node].son[1];
    initTree: Tree.Link;

    saveCd: ConsDestination = cd;
    cd ← [options: [init: TRUE, counted: counted], ignoreSafen: FALSE];  -- + defaults
    cd.remaining ← 1;
    tb[node].son[3] ← CountDups[tb[node].son[3]];
    
    IF counted THEN
      Counting.Allocate[zone: zoneTree, type: overType, catch: catchTree, pushSize: sizePusher]
    ELSE
      BEGIN
      P5.ZoneOp[zone: zoneTree, index: 0, pushArg: PushNewSize, catch: catchTree, long: long];
      Stack.Incr[pLength];
      END;
    IF tag # ISENull OR tb[node].son[3] # Tree.Null THEN
      BEGIN
      ptrVar: VarIndex;
      ptrVar ← P5L.TOSAddrLex[size: WordsForType[type], long: long].lexbdoi;
      [] ← SetConsDest[ptrVar];
      IF tag # ISENull THEN
	BEGIN
	offset:  VarComponent ← P5L.ComponentForSE[tag];
	WITH o: offset SELECT FROM
	  frame => ConsAssign[typeANY, @o, Tree.Null, [bdo[P5L.OVarItem[seqLength]]]];
	  ENDCASE => ERROR;
	END;
      IF tb[node].son[3] # Tree.Null THEN
	BEGIN
	offset:  VarComponent.frame ← [wSize: cd.wSize, bSize: cd.bSize, space: frame[]];
	initTree ← tb[node].son[3];
	DO
	  SELECT TreeOps.OpName[initTree] FROM
	    pad =>
	      BEGIN
	      initTree ← TreeOps.NthSon[initTree, 1];
	      offset.wSize ← P5U.WordsForOperand[initTree];  offset.bSize ← 0;
	      END;
	    cast => initTree ← TreeOps.NthSon[initTree, 1];
	    ENDCASE => EXIT;
	  ENDLOOP;
	SELECT TreeOps.OpName[initTree] FROM
	  construct =>
	    MainConstruct[TreeOps.NthSon[initTree, 2], P5U.OperandType[initTree], 
		RecField, @offset];
	  rowcons => Row[TreeOps.GetNode[initTree], @offset];
	  all => [] ← AllConstruct[TreeOps.GetNode[initTree], @offset];
	  mwconst => ConstantFill[type, @offset, initTree];
	  ENDCASE => {ConstructCountDown[]; ConsAssign[type, @offset, initTree]};
	END;
      IF cd.remaining # 1 THEN SIGNAL ConstructionError;
      [] ← LoadPointer[0];
      END;
    cd ← saveCd;
    RETURN [P5L.TOSLex[pLength]]
    END;
    
  END.