-- file Pass4Xa.mesa
-- last written by Satterthwaite, July 8, 1983 2:10 pm

DIRECTORY
  Alloc: TYPE USING [Notifier],
  ComData: TYPE USING [ownSymbols, switches, typeINT, typeCARDINAL, typeCHAR, zone],
  Environment: TYPE USING [bitsPerByte, bitsPerWord, maxCARDINAL, maxINTEGER],
  Inline: TYPE USING [BITAND, BITOR, BITSHIFT],
  Literals: TYPE USING [Base, LitDescriptor, ltType],
  LiteralOps: TYPE USING [FindDescriptor, MasterString],
  Log: TYPE USING [Error, ErrorN, ErrorTree],
  P4: TYPE USING [
    Attr, voidAttr, ConsState, Covering, Prop, emptyProp, voidProp,
    Repr, none, signed, unsigned, both, other, OpWordCount, RegCount, maxRegs,
    WordSeq, ValueDescriptor,
    checked, 
    AdjustBias, BiasForType, BitsForType, CatchNest, CommonProp, ComputeIndexRegs,
    Exp, ForceType, MakeStructuredLiteral, MakeTreeLiteral, MaxCardinality, NeutralExp,
    OperandType, RegsForType, RepForType, RValue, StructuredLiteral, Subst,
    TreeLiteral, TreeLiteralDesc, TreeLiteralValue, TypeExp, TypeForTree,
    VAttr, VBias, VPop, VProp, VPush, VRegs, VRep, WordsForType, ZeroP],
  Pass4: TYPE USING [implicitAttr, implicitBias, implicitType],
  Symbols: TYPE USING [
    Base, BitAddress, WordCount,
    Type, ISEIndex, CSEIndex, ArraySEIndex, RecordSEIndex,
    ISENull, RecordSENull, typeANY, ctxType, seType],
  SymbolOps: TYPE USING [
    ArgRecord, BitsPerElement, Cardinality, FirstVisibleSe, FnField, NextSe,
    RCType, RecordRoot, UnderType, VariantField],
  Tree: TYPE USING [Base, Index, Link, Map, Scan, Null, treeType],
  TreeOps: TYPE USING [
    FreeNode, FreeTree, GetNode, ListLength, NthSon, OpName, PopTree, PushTree,
    PushLit, PushNode, ScanList, SetAttr, SetInfo, UpdateList],
  Types: TYPE USING [Assignable];

Pass4Xa: PROGRAM
    IMPORTS
      Inline, Log, LiteralOps, P4, SymbolOps, TreeOps, Types,
      dataPtr: ComData, passPtr: Pass4
    EXPORTS P4 = {
  OPEN SymbolOps, TreeOps, P4;

 -- pervasive definitions from Symbols

  Type: TYPE = Symbols.Type;
  ISEIndex: TYPE = Symbols.ISEIndex;
  CSEIndex: TYPE = Symbols.CSEIndex;
  RecordSEIndex: TYPE = Symbols.RecordSEIndex;

  BitAddress: TYPE = Symbols.BitAddress;


  tb: Tree.Base;	-- tree base address (local copy)
  ltb: Literals.Base;	-- literal base address (local copy)
  seb: Symbols.Base;	-- se table base address (local copy)
  ctxb: Symbols.Base;	-- context table base address (local copy)

  ExpANotify: PUBLIC Alloc.Notifier = {
    -- called by allocator whenever table area is repacked
    tb ← base[Tree.treeType];  ltb ← base[Literals.ltType];
    seb ← base[Symbols.seType];  ctxb ← base[Symbols.ctxType]};


 -- expression list manipulation

  FieldRhs: PROC [t: Tree.Link, type: CSEIndex, cs: ConsState] RETURNS [Tree.Link] = {
    v: Tree.Link = Rhs[t, type, cs];
    RETURN [Safen[v, VProp[], cs, type]]};
      
  ConsOp: PROC [t: Tree.Link] RETURNS [BOOL] = {
    RETURN [SELECT OpName[t] FROM
      construct, union, rowcons, all => TRUE,
      cast, pad => ConsOp[NthSon[t, 1]],
      ENDCASE => FALSE]};
      
  Safen: PROC [t: Tree.Link, prop: Prop, cs: ConsState, type: CSEIndex] RETURNS [Tree.Link] = {
    PushTree[t];
    IF ~prop.noXfer OR (~prop.noAssign AND RCType[type] # none)
     OR (cs=$rest AND ~prop.noSelect AND ~prop.immutable) THEN
      IF ~ConsOp[t] THEN {
	PushNode[safen, 1]; SetInfo[type]; SetAttr[1, cs=$rest]; SetAttr[2, ~prop.noXfer]};
    RETURN [PopTree[]]};


  MakeRecord: PROC [record: RecordSEIndex, expList: Tree.Link, cs: ConsState]
      RETURNS [val: Tree.Link] = {
    sei: ISEIndex;
    const: BOOL ← TRUE;
    prop: Prop ← voidProp;
    nRegs: RegCount ← 0;

    EvaluateField: Tree.Map = {
      type: CSEIndex = UnderType[seb[sei].idType];
      IF t = Tree.Null THEN {
	v ← Tree.Null;
	IF BitsForType[type] # 0 OR VariantType[type] THEN const ← FALSE}
      ELSE {
	v ← FieldRhs[t, type, cs];
	IF ~TreeLiteral[v] THEN
	  WITH v SELECT FROM
	    subtree =>
	      SELECT tb[index].name FROM
	        mwconst => NULL;
	        union => IF ~tb[index].attr1 THEN const ← FALSE;
	        ENDCASE => const ← FALSE;
	    ENDCASE => const ← FALSE;
	prop ← CommonProp[VProp[], prop];  nRegs ← MAX[VRegs[], nRegs];  VPop[];
	IF cs = $first THEN cs ← $rest};
      sei ← NextSe[sei];
      RETURN};

    sei ← FirstVisibleSe[seb[record].fieldCtx];
    val ← UpdateList[expList, EvaluateField];
    IF OpName[val] = list THEN {
      subNode: Tree.Index = GetNode[val];
      tb[subNode].attr1 ← const};
    VPush[BiasForType[record], [prop: prop, rep: other], nRegs];
    RETURN};

    VariantType: PROC [type: CSEIndex] RETURNS [BOOL] = INLINE {
      RETURN [SELECT seb[type].typeTag FROM
	union, sequence => TRUE,
	ENDCASE => FALSE]};

  MakeArgRecord: PUBLIC PROC [record: RecordSEIndex, expList: Tree.Link]
      RETURNS [val: Tree.Link] = {
    SELECT TRUE FROM
      (expList = Tree.Null) => {val ← Tree.Null; VPush[0, voidAttr, 0]};
      (record = Symbols.RecordSENull) => {val ← FreeTree[expList]; VPush[0, voidAttr, 0]};
      (OpName[expList] = list) => val ← MakeRecord[record, expList, $init];
      ENDCASE => {
	type: CSEIndex = UnderType[seb[FirstVisibleSe[seb[record].fieldCtx]].idType];
	val ← FieldRhs[expList, type, $init]};
    RETURN};


  -- construction of packed values (machine dependent)

  WordLength: CARDINAL = Environment.bitsPerWord;
  ByteLength: CARDINAL = Environment.bitsPerByte;

 
  FillMultiWord: PUBLIC PROC [words: ValueDescriptor, origin: CARDINAL, t: Tree.Link] = {
    desc: Literals.LitDescriptor = TreeLiteralDesc[t];
    IF origin + desc.length <= words.length THEN
      FOR i: CARDINAL IN [0 .. desc.length) DO words[origin+i] ← ltb[desc.offset][i] ENDLOOP};

  PackRecord: PROC [record: RecordSEIndex, expList: Tree.Link] RETURNS [Tree.Link] = {
    n: CARDINAL = WordsForType[record];
    root, type: RecordSEIndex;
    list: Tree.Link;
    sei: ISEIndex;
    offset: CARDINAL;
    words: ValueDescriptor;
    more: BOOL;

    StoreBits: PROC [sei: ISEIndex, value: WORD] = {
      OPEN Inline;
      Masks: ARRAY [0..WordLength] OF WORD =
	[0b, 1b, 3b, 7b, 17b, 37b, 77b, 177b, 377b, 777b,
	 1777b, 3777b, 7777b, 17777b, 37777b, 77777b, 177777b];
      address: BitAddress;
      size, w, shift: CARDINAL;
      IF seb[root].argument THEN [address, size] ← FnField[sei]
      ELSE {address ← seb[sei].idValue; size ← seb[sei].idInfo};
      w ← address.wd;
      shift ← (WordLength-offset) - (address.bd+size);
      words[w] ← BITOR[words[w], BITSHIFT[BITAND[value, Masks[size]], shift]]};

    PackField: Tree.Scan = {
      node: Tree.Index;
      address: BitAddress;
      typeId: ISEIndex;
      subType: CSEIndex;
      SELECT TRUE FROM
	t = Tree.Null => NULL;
        TreeLiteral[t] => StoreBits[sei, TreeLiteralValue[t]];
	ENDCASE => {
	  node ← GetNode[t];
	  SELECT tb[node].name FROM
	    mwconst => {
	      address ← IF seb[root].argument
	        THEN FnField[sei].offset
		ELSE seb[sei].idValue;
	      FillMultiWord[words, address.wd, tb[node].son[1]]};
	    union => {
	      typeId ← NARROW[tb[node].son[1], Tree.Link.symbol].index;
	      subType ← UnderType[seb[sei].idType];
	      WITH seb[subType] SELECT FROM
		union => IF controlled THEN StoreBits[tagSei, seb[typeId].idValue];
		ENDCASE => ERROR;
	      type ← LOOPHOLE[UnderType[typeId], RecordSEIndex];
	      list ← tb[node].son[2];  more ← TRUE};
	    ENDCASE => ERROR};
      sei ← NextSe[sei]};

    words ← (dataPtr.zone).NEW[WordSeq[n]];
    FOR i: CARDINAL IN [0 .. n) DO words[i] ← 0 ENDLOOP;
    root ← type ← RecordRoot[record];
    offset ← IF seb[record].length < WordLength
		THEN WordLength - seb[record].length
		ELSE 0;
    list ← expList;  more ← TRUE;
    WHILE more DO
      more ← FALSE;  sei ← FirstVisibleSe[seb[type].fieldCtx];
      ScanList[list, PackField];
      ENDLOOP;
    PushLit[LiteralOps.FindDescriptor[DESCRIPTOR[words]]];
    PushNode[IF n=1 THEN cast ELSE mwconst, 1];  SetInfo[record];
    (dataPtr.zone).FREE[@words];
    RETURN [PopTree[]]};

  
  PadRecord: PUBLIC PROC [t: Tree.Link, lType: CSEIndex] RETURNS [Tree.Link] = {
    IF StructuredLiteral[t] THEN {
      nW: CARDINAL = WordsForType[lType];
      words: ValueDescriptor;
      node: Tree.Index;
      words ← (dataPtr.zone).NEW[WordSeq[nW]];
      FOR w: CARDINAL IN [0 .. nW) DO words[w] ← 0 ENDLOOP;
      IF TreeLiteral[t] THEN words[0] ← TreeLiteralValue[t]
      ELSE {
	node ← GetNode[t];
	SELECT tb[node].name FROM
	      mwconst => FillMultiWord[words, 0, tb[node].son[1]];
	      ENDCASE => ERROR;
	FreeNode[node]};
      PushLit[LiteralOps.FindDescriptor[DESCRIPTOR[words]]];  PushNode[mwconst, 1];
      (dataPtr.zone).FREE[@words]}
    ELSE {PushTree[t]; PushNode[pad, 1]};
    SetInfo[lType];
    RETURN [PopTree[]]};


  ExtractValue: PROC [t: Tree.Link, addr: BitAddress, size: CARDINAL, type: CSEIndex]
      RETURNS [val: Tree.Link] = {
    words: ValueDescriptor;
    desc: Literals.LitDescriptor = TreeLiteralDesc[t];
    n: CARDINAL = size/WordLength;
    IF n > 1 THEN {
      IF addr.bd # 0 THEN Log.Error[unimplemented];
      words ← (dataPtr.zone).NEW[WordSeq[n]];
      FOR i: CARDINAL IN [0..n) DO words[i] ← ltb[desc.offset][addr.wd+i] ENDLOOP;
      PushLit[LiteralOps.FindDescriptor[DESCRIPTOR[words]]];
      PushNode[mwconst, 1];  SetInfo[type];
      (dataPtr.zone).FREE[@words];
      val ← PopTree[]}
    ELSE
      val ← MakeStructuredLiteral[
		Inline.BITSHIFT[
		  Inline.BITSHIFT[ltb[desc.offset][addr.wd], addr.bd],
		  -(WordLength - size)],
		type];
    RETURN};


  UnpackField: PROC [t: Tree.Link, field: ISEIndex] RETURNS [val: Tree.Link] = {
    rType: CSEIndex = OperandType[t];
    vType: CSEIndex = UnderType[seb[field].idType];
    addr: BitAddress;
    addr ← seb[field].idValue;
    WITH r: seb[rType] SELECT FROM
      record =>
	IF r.length < WordLength THEN addr.bd ← addr.bd + (WordLength - r.length);
      ENDCASE => ERROR;
    RETURN [ExtractValue[t, addr, seb[field].idInfo, vType]]};

  UnpackElement: PROC [t: Tree.Link, i: CARDINAL] RETURNS [val: Tree.Link] = {
    aType: CSEIndex = OperandType[t];
    cType: CSEIndex;
    addr: BitAddress;
    nB, nW: CARDINAL;
    WITH a: seb[aType] SELECT FROM
      array => {
	cType ← UnderType[a.componentType];
	nB ← BitsPerElement[cType, a.packed];
	IF nB > ByteLength THEN {
	  nW ← (nB+(WordLength-1))/WordLength;
	  addr ← [wd:i*nW, bd:0];  nB ← nW*WordLength}
	ELSE {
	  itemsPerWord: CARDINAL = WordLength/nB;
	  offset: CARDINAL = IF WordsForType[aType] = 1
				THEN WordLength - CARDINAL[BitsForType[aType]]
				ELSE 0;
	  addr ← [wd: i/itemsPerWord, bd: offset + (i MOD itemsPerWord)*nB]}};
      ENDCASE => ERROR;
    RETURN [ExtractValue[t, addr, nB, cType]]};


 -- operators

  Substx: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = {
    type: CSEIndex = tb[node].info;
    IF OpName[tb[node].son[2]] = result THEN {
      saveChecked: BOOL = checked;
      subNode: Tree.Index = GetNode[tb[node].son[2]];
      IF ~tb[node].attr3 THEN checked ← tb[node].attr1;
      tb[node].son[1] ← NeutralExp[tb[node].son[1]];
      SELECT ListLength[tb[subNode].son[1]] FROM
	0 => ERROR;
	1 =>
	  val ← --IF tb[subNode].attr3
	    --THEN tb[subNode].son[1]
	    --ELSE-- ForceType[tb[subNode].son[1], type];
	ENDCASE => {
	  PushTree[Tree.Null];  PushTree[tb[subNode].son[1]];
	  PushNode[construct, 2];  SetInfo[type];  val ← PopTree[]};
      tb[subNode].son[1] ← Tree.Null;  FreeNode[node];
      val ← Rhs[val, type, $init];
      checked ← saveChecked}
    ELSE {
      val ← Subst[node];
      VPush[BiasForType[type], [prop: emptyProp, rep: RepForType[type]], maxRegs]};
    RETURN};


  Call: PUBLIC PROC [node: Tree.Index] RETURNS [Tree.Link] = {
    OPEN tb[node];
    type: CSEIndex;
    prop: Prop;
    son[1] ← Exp[son[1], none];  prop ← VProp[];  VPop[];
    type ← OperandType[son[1]];
    WITH t: seb[type] SELECT FROM
      transfer => {
	IF attr1 AND name # xerror AND t.typeIn # Symbols.RecordSENull THEN
	  son[2] ← Rhs[son[2], t.typeIn, $init]
	ELSE son[2] ← MakeArgRecord[ArgRecord[t.typeIn], son[2]];
	prop ← CommonProp[prop, VProp[]]; VPop[];
	prop.noXfer ← prop.noAssign ← prop.noFreeVar ← FALSE;
	IF nSons > 2 THEN CatchNest[son[3]];
	VPush[BiasForType[t.typeOut], [prop: prop, rep: RepForType[t.typeOut]], maxRegs]};
      ENDCASE => ERROR;
    RETURN [[subtree[index: node]]]};


  Construct: PUBLIC PROC [node: Tree.Index, cs: ConsState] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    type: RecordSEIndex = info;
    record: RecordSEIndex = RecordRoot[type];
    prop: Prop;
    nRegs: RegCount;
    k: RegCount = RegsForType[type];
    SELECT TRUE FROM
      (OpName[son[2]] = list) => {
	subNode: Tree.Index;
	son[2] ← MakeRecord[record, son[2], cs];  nRegs ← VRegs[];  prop ← VProp[];
	subNode ← GetNode[son[2]];
	IF ~tb[subNode].attr1 THEN {	-- ~all fields constant
	  tb[node].attr3 ← tb[subNode].attr3;
	  val ← [subtree[index: node]]; nRegs ← MAX[nRegs, k]}
	ELSE {val ← PackRecord[type, son[2]]; FreeNode[node]; nRegs ← k};
	VPop[];  VPush[0, [prop: prop, rep: other], nRegs]};
      (son[2] = Tree.Null) => {
        val ← Tree.Null;  VPush[0, [prop: voidProp, rep: other], k]};
      (OpName[son[2]] = union) => {
	son[2] ← Union[GetNode[son[2]], cs];
	IF OpName[son[2]] = union THEN {
	  subNode: Tree.Index = GetNode[son[2]];
	  IF tb[subNode].attr1 THEN {val ← PackRecord[type, son[2]]; FreeNode[node]}
	  ELSE val ← [subtree[index: node]]}
	ELSE {val ← ForceType[son[2], type]; son[2] ← Tree.Null; FreeNode[node]}};
      ENDCASE => val ← CastUniList[node, type, cs, record];
    RETURN};

  Union: PUBLIC PROC [node: Tree.Index, cs: ConsState] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    vSei: ISEIndex = NARROW[son[1], Tree.Link.symbol].index;
    type: RecordSEIndex = LOOPHOLE[UnderType[vSei]];
    tSei: CSEIndex = UnderType[info];
    tagged: BOOL =
      WITH seb[tSei] SELECT FROM union => controlled, ENDCASE => FALSE;
    attr: Attr;
    nRegs: RegCount;
    attr2 ← tagged;
    SELECT TRUE FROM
      (OpName[son[2]] = list OR OpName[son[2]] = union) => {
        son[2] ← MakeRecord[type, son[2], cs];  nRegs ← VRegs[];  attr ← VAttr[];
        attr1 ← WITH son[2] SELECT FROM subtree => tb[index].attr1, ENDCASE => FALSE;
        val ← [subtree[index: node]];  VPop[];  attr.rep ← other;  VPush[0, attr, nRegs]};
      (son[2] = Tree.Null) => {
	attr1 ← TRUE; val ← [subtree[index: node]];
	VPush[0, [prop: voidProp, rep: other], 1]};
      ENDCASE =>
	IF (~tagged OR seb[vSei].idValue = 0) AND WordsForType[type] = 1
	 AND ZeroOffset[IF tagged THEN TagSei[tSei] ELSE FirstVisibleSe[seb[type].fieldCtx]]
	 THEN val ← CastUniList[node, tSei, cs, type]
	ELSE {
	  son[2] ← MakeRecord[type, son[2], cs];  attr ← VAttr[];
          attr1 ← StructuredLiteral[son[2]];
	  val ← [subtree[index: node]];
	  VPop[];  attr.rep ← other;  VPush[0, attr, RegsForType[type]]};
    RETURN};

  TagSei: PROC [tSei: CSEIndex] RETURNS [ISEIndex] = INLINE {
    RETURN [WITH seb[tSei] SELECT FROM union => tagSei, ENDCASE => Symbols.ISENull]};

  ZeroOffset: PROC [sei: ISEIndex] RETURNS [BOOL] = INLINE {
    RETURN [sei # Symbols.ISENull AND seb[sei].idValue = BitAddress[0, 0]]};

  CastUniList: PROC [node: Tree.Index, type: CSEIndex, cs: ConsState, rType: RecordSEIndex]
      RETURNS [val: Tree.Link] = {
    target: CSEIndex = UnderType[seb[FirstVisibleSe[seb[rType].fieldCtx]].idType];
    prop: Prop;
    nRegs: RegCount;
    val ← ForceType[FieldRhs[tb[node].son[2], target, cs], type];
    prop ← VProp[];  nRegs ← VRegs[];  VPop[];
    tb[node].son[2] ← Tree.Null;  FreeNode[node];
    VPush[BiasForType[type], [prop: prop, rep: RepForType[type]], nRegs];
    RETURN};


  RowConstruct: PUBLIC PROC [node: Tree.Index, cs: ConsState] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    aType: Symbols.ArraySEIndex = info;
    cType: CSEIndex = UnderType[seb[aType].componentType];
    n: CARDINAL = Cardinality[seb[aType].indexType];
    const, strings, lstrings: BOOL;
    prop: Prop ← voidProp;
    nRegs: RegCount ← 0;
    l: CARDINAL;

    EvalElement: Tree.Map = {
      IF t = Tree.Null THEN {v ← Tree.Null; const ← strings ← lstrings ← FALSE}
      ELSE {
	v ← FieldRhs[t, cType, cs];
	IF TreeLiteral[v] THEN strings ← lstrings ← FALSE
	ELSE
	  WITH v SELECT FROM
	    subtree =>
	      SELECT tb[index].name FROM
		mwconst => strings ← lstrings ← FALSE;
		ENDCASE => const ← strings ← lstrings ← FALSE;
	    literal =>
	      WITH index SELECT FROM
		string => {
		  const ← FALSE;
		  IF LiteralOps.MasterString[sti] = sti THEN lstrings ← FALSE
		  ELSE strings ← FALSE};
		ENDCASE;
	    ENDCASE => const ← strings ← lstrings ← FALSE;
	prop ← CommonProp[VProp[], prop];  nRegs ← MAX[VRegs[], nRegs];  VPop[];
        IF cs = $first THEN cs ← $rest};
      RETURN};

    w, nW: CARDINAL;
    words: ValueDescriptor;
    bitsLeft: CARDINAL;
    bitCount: CARDINAL;

    PackElement: Tree.Scan = {
      IF TreeLiteral[t] THEN {
	bitsLeft ← bitsLeft - bitCount;
	words[w] ← Inline.BITOR[words[w],
		  Inline.BITSHIFT[TreeLiteralValue[t], bitsLeft]];
	IF bitsLeft < bitCount THEN {w ← w+1; bitsLeft ← WordLength}}
      ELSE {
	node: Tree.Index = GetNode[t];
	SELECT tb[node].name FROM
	  mwconst => {
	    FillMultiWord[words, w, tb[node].son[1]];
	    w ← w + WordsForType[cType]};
	  ENDCASE => ERROR}};

    SELECT (l ← ListLength[son[2]]) FROM
      = n => NULL;
      > n => Log.ErrorN[listLong, l-n];
      < n => Log.ErrorN[listShort, n-l];
      ENDCASE;
    const ← strings ← lstrings ← TRUE;  nRegs ← 0;
    son[2] ← UpdateList[son[2], EvalElement];
    IF const AND l = n THEN {
      nW ← WordsForType[aType];
      words ← (dataPtr.zone).NEW[WordSeq[nW]];
      FOR w: CARDINAL IN [0 .. nW) DO words[w] ← 0 ENDLOOP;
      bitCount ← BitsPerElement[cType, seb[aType].packed];
      w ← 0;  bitsLeft ← IF nW = 1 THEN CARDINAL[BitsForType[aType]] ELSE WordLength;
      ScanList[son[2], PackElement];  FreeNode[node];
      PushLit[LiteralOps.FindDescriptor[DESCRIPTOR[words]]];
      PushNode[IF nW = 1 THEN cast ELSE mwconst, 1];  SetInfo[aType];
      (dataPtr.zone).FREE[@words];
      val ← PopTree[];  nRegs ← RegsForType[aType]}
    ELSE {
      IF (attr1 ← strings # lstrings) THEN prop.noFreeVar ← FALSE;
      val ← [subtree[index: node]]};
    VPush[0, [prop: prop, rep: other], nRegs];  RETURN};

  All: PUBLIC PROC [node: Tree.Index, cs: ConsState] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    aType: Symbols.ArraySEIndex = info;
    cType: CSEIndex = UnderType[seb[aType].componentType];
    prop: Prop;
    val ← [subtree[index: node]];
    IF son[1] = Tree.Null THEN prop ← voidProp
    ELSE {
      son[1] ← FieldRhs[son[1], cType, cs];
      IF TreeLiteral[son[1]] AND WordsForType[aType] = 1 THEN {
	nB: CARDINAL = BitsPerElement[cType, seb[aType].packed];
	v, w: WORD;
	v ← TreeLiteralValue[son[1]];  w ← 0;
	THROUGH [1 .. Cardinality[seb[aType].indexType]] DO
	  w ← Inline.BITOR[Inline.BITSHIFT[w, nB], v] ENDLOOP;
	val ← ForceType[MakeTreeLiteral[w], aType];  FreeNode[node]}
      ELSE IF OperandType[son[1]] # cType THEN son[1] ← ForceType[son[1], cType];
      prop ← VProp[];  VPop[]};
    VPush[0, [prop: prop, rep: other], RegsForType[aType]];
    RETURN};


  Dot: PUBLIC PROC [node: Tree.Index, target: Repr] RETURNS [Tree.Link] = {
    OPEN tb[node];
    prop: Prop;
    attr: Attr;
    bias: INTEGER;
    nRegs: RegCount;
    son[1] ← RValue[son[1], 0, unsigned];
    prop ← VProp[];  prop.noSelect ← FALSE;
    nRegs ← MAX[RegsForType[info], VRegs[]];  VPop[];
    son[2] ← Exp[son[2], target];
    attr ← VAttr[];  bias ← VBias[];  VPop[];
    attr.prop ← CommonProp[attr.prop, prop];
    attr1 ← ~attr3 AND (checked OR dataPtr.switches['n]);
    VPush[bias, attr, nRegs];
    RETURN [[subtree[index: node]]]};
    
  Dollar: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    attr: Attr;
    immutable: BOOL;
    bias: INTEGER;
    nRegs: RegCount;
    k: RegCount = RegsForType[info];
    son[1] ← RValue[son[1], BiasForType[OperandType[son[1]]], none];
    nRegs ← VRegs[]; attr.prop ← VProp[]; immutable ← attr.prop.immutable; VPop[];
    son[2] ← Exp[son[2], none];  attr.rep ← VRep[];  bias ← VBias[];
    IF ~StructuredLiteral[son[1]] THEN {
      val ← [subtree[index: node]]; nRegs ← MAX[nRegs, k];
      attr.prop ← CommonProp[attr.prop, VProp[]];
      attr.prop.noSelect ← FALSE; attr.prop.immutable ← immutable}
    ELSE {
      val ← UnpackField[son[1], NARROW[son[2], Tree.Link.symbol].index];
      FreeNode[node]; nRegs ← k};
    VPop[];
    VPush[bias, attr, nRegs];  RETURN};


  Index: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = {
    OPEN tb[node];
    iType, cType: CSEIndex;
    next: Type;
    prop: Prop;
    immutable: BOOL;
    nRegs: RegCount;
    son[1] ← Exp[son[1], none];  prop ← VProp[];  immutable ← prop.immutable;
    FOR aType: CSEIndex ← OperandType[son[1]], UnderType[next] DO
      WITH seb[aType] SELECT FROM
	array => {
	  iType ← UnderType[indexType]; cType ← UnderType[componentType]; EXIT};
	arraydesc => next ← describedType;
	long => next ← rangeType;
	ENDCASE => ERROR;
      ENDLOOP;
    IF WordsForType[cType] > OpWordCount.LAST THEN
      Log.ErrorTree[addressOverflow, [subtree[node]]];
    IF name = dindex THEN {
      son[2] ← RValue[son[2], BiasForType[iType], unsigned];
      attr1 ← checked OR dataPtr.switches['n];
      attr3 ← checked OR dataPtr.switches['b]}
    ELSE son[2] ← Rhs[son[2], iType, $init, TRUE];
    prop ← CommonProp[prop, VProp[]];
    SELECT TRUE FROM
      (TreeLiteral[son[2]] AND OpName[son[1]] = all) => {
	subNode: Tree.Index = GetNode[son[1]];
	val ← tb[subNode].son[1];
	tb[subNode].son[1] ← Tree.Null;  FreeNode[node];
	nRegs ← RegsForType[cType]};
      (TreeLiteral[son[2]] AND StructuredLiteral[son[1]]) => {
	val ← UnpackElement[son[1], TreeLiteralValue[son[2]]]; FreeNode[node];
	nRegs ← RegsForType[cType]};
      ENDCASE => {
	val ← [subtree[index:node]];
	nRegs ← ComputeIndexRegs[node];
	prop.noSelect ← FALSE; prop.immutable ← immutable};
    VPop[]; VPop[];
    VPush[BiasForType[cType], [prop: prop, rep: RepForType[cType]], nRegs];
    RETURN};

  SeqIndex: PUBLIC PROC [node: Tree.Index] RETURNS [Tree.Link] = {
    OPEN tb[node];
    iType, cType, sType: CSEIndex;
    prop: Prop;
    nRegs: RegCount;
    son[1] ← Exp[son[1], none];  prop ← VProp[];
    sType ← OperandType[son[1]];
    WITH t: seb[sType] SELECT FROM
      sequence => {
	iType ← UnderType[seb[t.tagSei].idType]; cType ← UnderType[t.componentType];
	attr3 ← t.controlled AND (checked OR dataPtr.switches['b])};
      array => {
	iType ← UnderType[t.indexType]; cType ← UnderType[t.componentType];
	attr3 ← checked OR dataPtr.switches['b]};
      ENDCASE;
    IF WordsForType[cType] > OpWordCount.LAST THEN
      Log.ErrorTree[addressOverflow, [subtree[node]]];
    son[2] ← RValue[son[2], BiasForType[iType], TargetRep[RepForType[iType]]];
    nRegs ← ComputeIndexRegs[node];
    prop ← CommonProp[prop, VProp[]]; prop.noSelect ← FALSE;  VPop[]; VPop[];
    VPush[BiasForType[cType], [prop: prop, rep: RepForType[cType]], nRegs];
    RETURN [[subtree[index:node]]]};

  Reloc: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = {
    prop: Prop;
    nRegs: RegCount;
    type: CSEIndex = tb[node].info;
    tb[node].son[1] ← RValue[tb[node].son[1], 0, unsigned];  prop ← VProp[];
    tb[node].son[2] ← RValue[tb[node].son[2], 0, unsigned];  prop ← CommonProp[prop, VProp[]];
    nRegs ← ComputeIndexRegs[node];  VPop[]; VPop[];
    IF ~tb[node].attr1 AND ZeroP[tb[node].son[1]] THEN {
      rType, subType, next: CSEIndex;
      FOR subType ← OperandType[tb[node].son[2]], next DO    -- CanonicalType
	WITH r: seb[subType] SELECT FROM
	  relative => {
	    rType ← UnderType[r.resultType];
	    PushTree[tb[node].son[2]];  tb[node].son[2] ← Tree.Null;
	    IF tb[node].attr2 AND seb[UnderType[r.offsetType]].typeTag # long THEN {
	      PushNode[lengthen, 1];
	      SetAttr[1, FALSE]; SetAttr[2, FALSE]; SetAttr[3, FALSE]}
	    ELSE PushNode[cast, 1];
	    EXIT};
	  record => next ← UnderType[seb[FirstVisibleSe[r.fieldCtx]].idType];
	  ENDCASE => ERROR;
	ENDLOOP;
      SetInfo[rType];
      PushNode[uparrow, 1];  SetInfo[type];
      SetAttr[1, dataPtr.switches['n]];  SetAttr[2, tb[node].attr2];
      val ← PopTree[];  FreeNode[node]}
    ELSE val ← [subtree[node]];
    prop.noSelect ← FALSE;
    VPush[BiasForType[type], [prop: prop, rep: RepForType[type]], nRegs];
    RETURN};


  Assignment: PUBLIC PROC [node: Tree.Index] RETURNS [Tree.Link] = {
    OPEN tb[node];
    lhsType: CSEIndex;
    bias: INTEGER;
    attr: Attr;
    nRegs: RegCount;
    son[1] ← Exp[son[1], none];
    bias ← VBias[]; attr ← VAttr[]; nRegs ← VRegs[];
    lhsType ← OperandType[son[1]];
    son[2] ← Rhs[son[2], lhsType, $first];
    attr.prop ← CommonProp[attr.prop, VProp[]]; attr.prop.noAssign ← FALSE;
    VPop[]; VPop[];  VPush[bias, attr, nRegs];
    RETURN [RewriteAssign[node, lhsType]]};

  Extract: PUBLIC PROC [node: Tree.Index] RETURNS [Tree.Link] = {
    subNode: Tree.Index = GetNode[tb[node].son[1]];
    rType: RecordSEIndex = tb[subNode].info;
    prop: Prop ← voidProp;
    sei: ISEIndex;

    AssignItem: Tree.Map = {
      type: CSEIndex;
      saveType: CSEIndex = passPtr.implicitType;
      saveBias: INTEGER = passPtr.implicitBias;
      saveAttr: Attr = passPtr.implicitAttr;
      IF t = Tree.Null THEN v ← Tree.Null
      ELSE {
	subNode: Tree.Index = GetNode[t];
	type ← UnderType[seb[sei].idType];
	passPtr.implicitType ← type;
	passPtr.implicitBias ← BiasForType[type];  passPtr.implicitAttr.rep ← RepForType[type];
	v ← IF tb[subNode].name = extract THEN Extract[subNode] ELSE Assignment[subNode];
	prop ← CommonProp[prop, VProp[]];  VPop[]};
      sei ← NextSe[sei];
      passPtr.implicitAttr ← saveAttr;  passPtr.implicitBias ← saveBias;
      passPtr.implicitType ← saveType;  RETURN};

    sei ← FirstVisibleSe[seb[rType].fieldCtx];
    tb[subNode].son[1] ← UpdateList[tb[subNode].son[1], AssignItem];
    tb[node].son[2] ← Exp[tb[node].son[2], none];  prop ← CommonProp[prop, VProp[]];  VPop[];
    VPush[BiasForType[rType], [prop: prop, rep: RepForType[rType]], maxRegs];
    RETURN [[subtree[index:node]]]};


  New: PUBLIC PROC [node: Tree.Index] RETURNS [Tree.Link] = {
    OPEN tb[node];
    prop: Prop ← voidProp;
    IF son[1] # Tree.Null THEN {son[1] ← Exp[son[1], none]; prop ← VProp[]; VPop[]};
    IF OpName[son[2]] = apply THEN {
      subNode: Tree.Index = GetNode[son[2]];
      type: CSEIndex;
      vSei: ISEIndex;
      TypeExp[tb[subNode].son[1]];  type ← UnderType[TypeForTree[tb[subNode].son[1]]];
      tb[subNode].son[2] ← Rhs[tb[subNode].son[2], dataPtr.typeCARDINAL, $init];
      prop ← CommonProp[prop, VProp[]];  VPop[];
      vSei ← VariantField[type];
      IF vSei # Symbols.ISENull THEN {
	vType: CSEIndex = UnderType[seb[vSei].idType];
	subType: CSEIndex = OperandType[tb[subNode].son[2]];
	n: LONG CARDINAL = WITH t: seb[vType] SELECT FROM
	    sequence => MIN[
		Cardinality[seb[t.tagSei].idType],
		MaxCardinality[t.componentType, t.packed, OpWordCount.LAST-WordsForType[type]]]
	    ENDCASE => 0;
	IF subType = dataPtr.typeINT OR ~(Cardinality[subType] IN [1..n]) THEN  -- (0..n]
	  tb[subNode].son[2] ← CheckRange[tb[subNode].son[2], n, dataPtr.typeCARDINAL]}}
    ELSE {
      TypeExp[son[2], OpName[son[3]] = body];
      IF WordsForType[UnderType[TypeForTree[son[2]]]] > OpWordCount.LAST THEN
        Log.ErrorTree[unimplemented, [subtree[node]]]};
    SELECT OpName[son[3]] FROM
      body => {
	expNode: Tree.Index = GetNode[son[3]];
	PushNode[body, 0];  SetInfo[tb[expNode].info];  son[3] ← PopTree[]};
      signalinit => NULL;
      ENDCASE =>
	IF son[3] # Tree.Null THEN {
	  type: CSEIndex = UnderType[TypeForTree[son[2]]];
	  subProp: Prop;
	  son[3] ← Rhs[son[3], type, $init];  subProp ← VProp[];  VPop[];
	  IF attr3 THEN son[3] ← Safen[son[3], subProp, $init, type];
	  prop ← CommonProp[prop, subProp]};
    IF nSons > 3 THEN CatchNest[son[4]];
    prop.noXfer ← prop.noFreeVar ← FALSE;  VPush[0, [prop: prop, rep: unsigned], maxRegs];
    RETURN [[subtree[index:node]]]};


  Narrow: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = {
    type: CSEIndex = tb[node].info;
    IF tb[node].son[2] # Tree.Null THEN TypeExp[tb[node].son[2]];
    IF tb[node].attr2 OR tb[node].attr3 THEN {
      OPEN tb[node];
      prop: Prop;
      son[1] ← RValue[son[1], 0, RepForType[OperandType[son[1]]]];
      prop ← VProp[];  prop.noXfer ← FALSE;  VPop[];
      IF nSons > 2 THEN CatchNest[son[3]];
      val ← [subtree[index: node]];
      VPush[BiasForType[type], [prop: prop, rep: RepForType[type]], maxRegs]}
    ELSE {
      val ← Rhs[tb[node].son[1], type, $init];
      tb[node].son[1] ← Tree.Null;  FreeNode[node]}};


  TargetRep: --PUBLIC-- PROC [rep: Repr] RETURNS [Repr] = INLINE {
    RETURN [--IF rep = both THEN signed ELSE-- rep]};


  Rhs: PUBLIC PROC [exp: Tree.Link, lType: CSEIndex, cs: ConsState, voidOK: BOOL←FALSE]
      RETURNS [val: Tree.Link] = {
    lBias: INTEGER = BiasForType[lType];
    lRep: Repr = RepForType[lType];
    nw: Symbols.WordCount = WordsForType[lType];
    rType: CSEIndex = OperandType[exp];
    rRep: Repr;
    WITH exp SELECT FROM
      subtree => {
        node: Tree.Index = index;
        val ← SELECT tb[node].name FROM
          construct => Construct[node, cs],
          union => Union[node, cs],
          rowcons => RowConstruct[node, cs],
          all => All[node, cs],
          ENDCASE => RValue[exp, lBias, TargetRep[lRep]]};
      ENDCASE => val ← RValue[exp, lBias, TargetRep[lRep]];
    rRep ← VRep[];
    IF ~Types.Assignable[[dataPtr.ownSymbols, lType], [dataPtr.ownSymbols, rType]] THEN
      Log.ErrorTree[typeClash, val];
    IF ~(IF nw = 0 THEN voidOK ELSE WordsForType[rType] = nw) THEN
      SELECT TRUE FROM
	(seb[lType].typeTag = record) AND (seb[rType].typeTag = record) =>
	  val ← PadRecord[val, lType];
	(seb[lType].typeTag = union) AND (seb[rType].typeTag = union) => NULL;
	ENDCASE => Log.ErrorTree[sizeClash, val];
    IF nw > OpWordCount.LAST THEN Log.ErrorTree[unimplemented, val];
    IF (lType = dataPtr.typeINT AND rRep = unsigned) OR
       ((rType = dataPtr.typeINT AND rRep = signed) AND lRep = unsigned) THEN
      val ← CheckRange[val, CARDINAL[Environment.maxINTEGER-lBias]+1, lType]
    ELSE
      SELECT seb[lType].typeTag FROM
	subrange, enumerated, relative =>
	  SELECT Cover[lType, lRep, rType, rRep] FROM
	    full => NULL;
	    partial => val ← CheckRange[val, Cardinality[lType], lType];
	    ENDCASE => IF nw # 0 THEN val ← BoundsFault[val, lType];
	basic =>
	  IF lType = dataPtr.typeCHAR AND (rRep # both OR TreeLiteral[val]) THEN
	    val ← CheckRange[val, Cardinality[lType], lType];
	long =>
	  IF (lRep=signed AND rRep=unsigned) OR (lRep=unsigned AND rRep=signed) THEN
	    val ← CheckRange[val, CARDINAL[Environment.maxINTEGER]+1, lType];
	ENDCASE => NULL;
    RETURN};

    
  Cover: PUBLIC PROC [lType: CSEIndex, lRep: Repr, rType: CSEIndex, rRep: Repr]
      RETURNS [Covering] = {
    lLb, lUb, rLb, rUb: LONG INTEGER;
    [lLb, lUb] ← Bounds[lType, lRep];
    [rLb, rUb] ← Bounds[rType, rRep];
    RETURN [
      IF lLb <= rLb
	THEN IF lUb < rLb THEN none ELSE IF lUb < rUb THEN partial ELSE full
	ELSE IF lLb <= rUb THEN partial ELSE none]};

  Bounds: PROC [type: CSEIndex, rep: Repr] RETURNS [lb, ub: LONG INTEGER] = {
    WITH t: seb[type] SELECT FROM
      subrange => {lb ← t.origin; ub ← lb + t.range};
      enumerated => {lb ← 0; ub ← t.nValues-1};
      relative => [lb, ub] ← Bounds[UnderType[t.offsetType], rep];
      ENDCASE =>
	SELECT rep FROM
	  signed => {lb ← -Environment.maxINTEGER-1; ub ← Environment.maxINTEGER};
	  both => {lb ← 0;  ub ← Environment.maxINTEGER};
	  ENDCASE => {lb ← 0;  ub ← Environment.maxCARDINAL};
    RETURN};

  CheckRange: PUBLIC PROC [t: Tree.Link, bound: CARDINAL, type: CSEIndex]
      RETURNS [val: Tree.Link] = {
    SELECT TRUE FROM
      (bound = 0) => val ← t;
      TreeLiteral[t] =>
	val ← IF TreeLiteralValue[t] >= bound THEN BoundsFault[t,type] ELSE t;
      (checked OR dataPtr.switches['b]) AND ~Bounded[t, bound] => {
	PushTree[MakeTreeLiteral[bound]];  PushTree[t];
	PushNode[check,-2];  SetInfo[type];  val ← PopTree[]};
      ENDCASE => val ← t;
    RETURN};

  Bounded: PROC [t: Tree.Link, bound: CARDINAL] RETURNS [BOOL] = INLINE {
    IF OpName[t] = mod THEN {
      t2: Tree.Link = NthSon[t, 2];
      RETURN [TreeLiteral[t2] AND TreeLiteralValue[t2] IN [0..bound]]}
    ELSE RETURN [FALSE]};

  BoundsFault: PROC [t: Tree.Link, type: CSEIndex] RETURNS [Tree.Link] = {
    Log.ErrorTree[boundsFault, AdjustBias[t, -BiasForType[type]]];
    PushTree[t];
--  PushTree[MakeTreeLiteral[0]]; PushNode[check, 2]; SetInfo[type];
    RETURN [PopTree[]]};


  RewriteAssign: PUBLIC PROC [node: Tree.Index, lType: CSEIndex] RETURNS [Tree.Link] = {
    IF seb[lType].typeTag = union THEN {
      WITH tb[node].son[1] SELECT FROM
	subtree => {
	  subType: CSEIndex;
	  subNode: Tree.Index = index;
	  SELECT tb[subNode].name FROM
	    dot => {
	      subType ← OperandType[tb[subNode].son[1]];
	      PushTree[tb[subNode].son[1]];  PushNode[uparrow, 1];
	      SetInfo[WITH seb[subType] SELECT FROM
			ref => UnderType[refType],
			ENDCASE => Symbols.typeANY];
	      tb[subNode].son[1] ← PopTree[];
	      tb[subNode].name ← dollar};
	    dollar => NULL;
	    ENDCASE => NULL};	-- flagged by code generators for now
	ENDCASE => NULL};	-- flagged by code generators for now
    IF tb[node].name = assignx THEN tb[node].info ← OperandType[tb[node].son[1]];
    RETURN [[subtree[index: node]]]};

  }.