(* M2GenGCC.mod convert the quadruples into GCC trees.

Copyright (C) 2001-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  *)

IMPLEMENTATION MODULE M2GenGCC ;

FROM SYSTEM IMPORT ADDRESS, WORD ;

FROM SymbolTable IMPORT PushSize, PopSize, PushValue, PopValue,
                        PushVarSize,
                        MakeConstLit,
                        RequestSym, FromModuleGetSym,
                        StartScope, EndScope, GetScope,
                        GetMainModule, GetModuleScope,
                        GetSymName, ModeOfAddr, GetMode,
                        GetGnuAsm, IsGnuAsmVolatile, IsGnuAsmSimple,
                        GetGnuAsmInput, GetGnuAsmOutput, GetGnuAsmTrash,
                        GetLowestType,
                        GetLocalSym, GetVarWritten,
                        GetVarient, GetVarBackEndType, GetModuleCtors,
                        NoOfVariables,
                        NoOfParamAny, GetParent, GetDimension, IsAModula2Type,
                        IsModule, IsDefImp, IsType, IsModuleWithinProcedure,
                        IsConstString, GetString, GetStringLength,
                        IsConstStringCnul, IsConstStringM2nul,
                        IsConst, IsConstSet, IsProcedure, IsProcType,
                        IsVar, IsVarParamAny, IsTemporary, IsTuple,
                        IsEnumeration,
                        IsUnbounded, IsArray, IsSet, IsConstructor, IsConstructorConstant,
                        IsProcedureVariable,
                        IsUnboundedParamAny,
                        IsRecordField, IsFieldVarient, IsVarient, IsRecord,
                        IsExportQualified,
                        IsExported,
                        IsSubrange, IsPointer,
                        IsProcedureBuiltinAvailable, IsProcedureInline,
                        IsParameter, IsParameterVar,
                        IsValueSolved, IsSizeSolved,
                        IsProcedureNested, IsInnerModule, IsArrayLarge,
                        IsComposite, IsVariableSSA, IsPublic, IsCtor,
                        IsConstStringKnown,
                        ForeachExportedDo,
                        ForeachImportedDo,
                        ForeachProcedureDo,
                        ForeachInnerModuleDo,
                        ForeachLocalSymDo,
			GetLType, GetDType,
                        GetType, GetNth, GetNthParamAny,
                        SkipType, SkipTypeAndSubrange,
                        GetUnboundedHighOffset,
                        GetUnboundedAddressOffset,
                        GetSubrange, NoOfElements, GetArraySubscript,
                        GetFirstUsed, GetDeclaredMod,
                        GetProcedureBeginEnd,
                        GetRegInterface,
                        GetProcedureQuads,
                        GetProcedureBuiltin,
                        GetPriority, GetNeedSavePriority,
                        PutConstStringKnown,
                        PutConst, PutConstSet, PutConstructor,
			GetSType, GetTypeMode,
                        HasVarParameters, CopyConstString,
                        GetVarDeclFullTok,
                        NulSym ;

FROM M2Batch IMPORT MakeDefinitionSource ;

FROM M2LexBuf IMPORT FindFileNameFromToken, TokenToLineNo, TokenToLocation,
                     MakeVirtualTok, UnknownTokenNo, BuiltinTokenNo ;

FROM M2Code IMPORT CodeBlock ;
FROM M2Debug IMPORT Assert ;
FROM M2Error IMPORT InternalError, WriteFormat0, WriteFormat1, WriteFormat2, WarnStringAt ;

FROM M2MetaError IMPORT MetaErrorT0, MetaErrorT1, MetaErrorT2, MetaErrorT3,
                        MetaError1, MetaError2, MetaErrorStringT1,
                        MetaErrorDecl ;

FROM M2Options IMPORT UnboundedByReference, PedanticCast,
                      VerboseUnbounded, Iso, Pim, DebugBuiltins, WholeProgram,
                      StrictTypeChecking, AutoInit, cflag, ScaffoldMain,
                      ScaffoldDynamic, ScaffoldStatic, GetDebugTraceQuad ;

FROM M2Printf IMPORT printf0, printf1, printf2, printf4 ;
FROM M2Quiet IMPORT qprintf0 ;

FROM M2Base IMPORT MixTypes, MixTypesDecl, NegateType, ActivationPointer, IsMathType,
                   IsRealType, IsComplexType, IsBaseType,
                   IsOrdinalType,
                   Cardinal, Char, Integer, IsTrunc,
                   Boolean, True,
                   Im, Re, Cmplx, GetCmplxReturnType, GetBaseTypeMinMax,
                   CheckAssignmentCompatible,
                   IsAssignmentCompatible, IsExpressionCompatible ;

FROM M2Bitset IMPORT Bitset ;
FROM NameKey IMPORT Name, MakeKey, KeyToCharStar, LengthKey, makekey, NulName ;

FROM DynamicStrings IMPORT string, InitString, KillString, String,
                           InitStringCharStar, Mark, Slice, ConCat, ConCatChar,
                           InitStringChar, Dup ;

FROM FormatStrings IMPORT Sprintf0, Sprintf1, Sprintf2, Sprintf3, Sprintf4 ;
FROM M2System IMPORT Address, Word, System, TBitSize, MakeAdr, IsSystemType, IsGenericSystemType, IsRealN, IsComplexN, IsSetN, IsWordN, Loc, Byte ;
FROM M2FileName IMPORT CalculateFileName ;
FROM SymbolConversion IMPORT AddModGcc, Mod2Gcc, GccKnowsAbout, RemoveMod2Gcc ;

FROM M2StackWord IMPORT InitStackWord, StackOfWord, PeepWord, ReduceWord,
                        PushWord, PopWord, IsEmptyWord ;

FROM Lists IMPORT List, InitList, KillList,
                  PutItemIntoList,
                  RemoveItemFromList, IncludeItemIntoList,
                  NoOfItemsInList, GetItemFromList ;

FROM M2ALU IMPORT PtrToValue,
                  IsValueTypeReal, IsValueTypeSet,
                  IsValueTypeConstructor, IsValueTypeArray,
                  IsValueTypeRecord, IsValueTypeComplex,
                  PushIntegerTree, PopIntegerTree,
                  PushSetTree, PopSetTree,
                  PopRealTree, PushCard,
                  PushRealTree,
                  PopComplexTree, PopChar,
                  Gre, Sub, Equ, NotEqu, LessEqu,
                  BuildRange, SetOr, SetAnd, SetNegate,
                  SetSymmetricDifference, SetDifference,
                  SetShift, SetRotate,
                  AddBit, SubBit, Less, Addn, GreEqu, SetIn,
                  CheckOrResetOverflow, GetRange, GetValue,
                  ConvertToType ;

FROM M2GCCDeclare IMPORT WalkAction,
                         DeclareConstant, TryDeclareConstant, TryDeclareType,
                         DeclareConstructor, TryDeclareConstructor,
                         StartDeclareScope, EndDeclareScope,
                         PromoteToString, PromoteToCString, DeclareLocalVariable,
                         CompletelyResolved,
                         PoisonSymbols, GetTypeMin, GetTypeMax,
                         IsProcedureGccNested, DeclareParameters,
                         ConstantKnownAndUsed, PrintSym ;

FROM M2Range IMPORT CodeRangeCheck, FoldRangeCheck, CodeErrorCheck, GetMinMax ;

FROM m2builtins IMPORT BuiltInAlloca,
                       BuiltinMemSet, BuiltinMemCopy,
                       GetBuiltinConst, GetBuiltinTypeInfo,
                       BuiltinExists, BuildBuiltinTree ;

FROM m2expr IMPORT GetIntegerZero, GetIntegerOne,
                   GetCardinalOne,
                   GetPointerZero,
                   GetCardinalZero,
                   GetSizeOfInBits,
                   TreeOverflow,
                   FoldAndStrip,
                   CompareTrees,
                   StringLength,
                   AreConstantsEqual,
                   GetCstInteger,
                   BuildForeachWordInSetDoIfExpr,
                   BuildIfConstInVar,
                   BuildIfVarInVar,
                   BuildIfNotConstInVar,
                   BuildIfNotVarInVar,
                   BuildBinCheckProcedure, BuildUnaryCheckProcedure,
                   BuildBinProcedure, BuildUnaryProcedure,
                   BuildSetProcedure, BuildUnarySetFunction,
		   BuildAddCheck, BuildSubCheck, BuildMultCheck, BuildDivTruncCheck,
                   BuildDivM2Check, BuildModM2Check,
                   BuildAdd, BuildSub, BuildMult, BuildLSL,
		   BuildDivCeil, BuildModCeil,
                   BuildDivTrunc, BuildModTrunc, BuildDivFloor, BuildModFloor,
		   BuildDivM2, BuildModM2,
                   BuildRDiv,
                   BuildLogicalOrAddress,
                   BuildLogicalOr, BuildLogicalAnd, BuildSymmetricDifference,
                   BuildLogicalDifference,
                   BuildLogicalShift, BuildLogicalRotate,
                   BuildNegate, BuildNegateCheck, BuildAddr, BuildSize,
                   BuildTBitSize, BuildSystemTBitSize,
                   BuildOffset, BuildOffset1,
                   BuildLessThan, BuildGreaterThan,
                   BuildLessThanOrEqual, BuildGreaterThanOrEqual,
                   BuildEqualTo, BuildNotEqualTo,
                   BuildIsSuperset, BuildIsNotSuperset,
                   BuildIsSubset, BuildIsNotSubset,
                   BuildIndirect, BuildArray,
                   BuildTrunc, BuildCoerce,
                   BuildBinaryForeachWordDo,
                   BuildBinarySetDo,
                   BuildSetNegate,
                   BuildComponentRef,
                   BuildCap, BuildAbs, BuildIm, BuildRe, BuildCmplx,
                   BuildAddAddress,
                   BuildIfInRangeGoto, BuildIfNotInRangeGoto ;

FROM m2tree IMPORT debug_tree, skip_const_decl ;
FROM gcctypes IMPORT location_t, tree ;

FROM m2decl IMPORT BuildStringConstant, BuildCStringConstant,
                   DeclareKnownConstant, GetBitsPerBitset,
                   BuildIntegerConstant,
                   BuildModuleCtor, DeclareModuleCtor ;

FROM m2statement IMPORT BuildAsm, BuildProcedureCallTree, BuildParam, BuildFunctValue,
                        DoJump, BuildUnaryForeachWordDo, BuildGoto, BuildCall2, BuildCall3,
                        BuildStart, BuildEnd, BuildCallInner, BuildStartFunctionCode,
                        BuildEndFunctionCode,
                        BuildAssignmentTree, DeclareLabel,
                        BuildFunctionCallTree,
                        BuildAssignmentStatement,
                        BuildIndirectProcedureCallTree,
                        BuildPushFunctionContext, BuildPopFunctionContext,
                        BuildReturnValueCode, SetLastFunction,
                        BuildIncludeVarConst, BuildIncludeVarVar,
                        BuildExcludeVarConst, BuildExcludeVarVar,
                        BuildBuiltinCallTree, CopyByField,
			GetParamTree, BuildCleanUp,
			BuildTryFinally,
			GetLastFunction, SetLastFunction,
                        SetBeginLocation, SetEndLocation ;

FROM m2type IMPORT ChainOnParamValue, GetPointerType, GetIntegerType, AddStatement,
                   GetCardinalType, GetWordType, GetM2ZType, GetM2RType, GetM2CType,
                   BuildCharConstant, AddStringToTreeList, BuildArrayStringConstructor,
                   GetArrayNoOfElements, GetTreeType, IsGccStrictTypeEquivalent ;

FROM m2block IMPORT RememberConstant, pushGlobalScope, popGlobalScope, finishFunctionDecl,
                    pushFunctionScope, popFunctionScope,
		    push_statement_list, pop_statement_list, begin_statement_list,
		    addStmtNote, removeStmtNote ;

FROM m2misc IMPORT DebugTree ;

FROM m2convert IMPORT BuildConvert, ConvertConstantAndCheck, ToCardinal, ConvertString ;

FROM m2except IMPORT BuildThrow, BuildTryBegin, BuildTryEnd,
                     BuildCatchBegin, BuildCatchEnd ;

FROM M2Quads IMPORT QuadOperator, GetQuad, IsReferenced, GetNextQuad,
                    SubQuad, PutQuad, MustCheckOverflow, GetQuadOtok,
                    GetQuadOTypetok,
                    QuadToTokenNo, DisplayQuad, GetQuadtok,
                    GetM2OperatorDesc, GetQuadOp,
                    IsQuadConstExpr, IsBecomes, IsGoto, IsConditional,
                    IsDummy, IsConditionalBooleanQuad,
                    GetQuadOp1, GetQuadOp3, GetQuadDest, SetQuadConstExpr ;

FROM M2Check IMPORT ParameterTypeCompatible, AssignmentTypeCompatible,  ExpressionTypeCompatible ;
FROM M2SSA IMPORT EnableSSA ;
FROM M2Optimize IMPORT FoldBranches ;

FROM M2BasicBlock IMPORT BasicBlock, IsBasicBlockFirst,
                         GetBasicBlockStart, GetBasicBlockEnd ;


CONST
   Debugging         = FALSE ;
   PriorityDebugging = FALSE ;
   CascadedDebugging = FALSE ;

TYPE
   DoProcedure      = PROCEDURE (CARDINAL) ;
   DoUnaryProcedure = PROCEDURE (CARDINAL) ;

VAR
   Memset, Memcpy           : CARDINAL ;
   CurrentQuadToken         : CARDINAL ;
   UnboundedLabelNo         : CARDINAL ;
   LastLine                 : CARDINAL ;(* The Last Line number emitted with the  *)
                                        (* generated code.                        *)
   LastOperator             : QuadOperator ; (* The last operator processed.      *)
   ScopeStack               : StackOfWord ; (* keeps track of the current scope       *)
                                            (* under translation.                     *)
   NoChange                 : BOOLEAN ;     (* has any constant been resolved?        *)


(*
   Rules for Quadruples
   ====================

   Rules
   =====

   All program declared variables are given the mode, Offset.
   All constants have mode, Immediate.

   Operators
   =========

------------------------------------------------------------------------------
   Array Operators
------------------------------------------------------------------------------
   Sym<I>   Base   a            Delivers a constant result if a is a
                                Global variable. If a is a local variable
                                then the Frame pointer needs to be added.
                                Base yields the effective location in memory
                                of, a, array [0,0, .. ,0] address.
   Sym<I>   ElementSize 1       Always delivers a constant. The number
                                indicates which specified element is chosen.
                                ElementSize is the TypeSize for that element.
   Unbounded  Op1 Op3           Initializes the op1 StartAddress of the array
                                op3. Op3 can be a normal array or unbounded array.
                                op1 (is the Unbounded.ArrayAddress) := ADR(op3).
                                In GNU Modula-2 the callee saves non var unbounded
                                arrays. This is direct contrast to the M2F native
                                code generators.
------------------------------------------------------------------------------
   := Operator
------------------------------------------------------------------------------
   Sym1<I> := Sym3<I>           := produces a constant
   Sym1<O> := Sym3<O>           := has the effect Mem[Sym1<I>] := Mem[Sym3<I>]
------------------------------------------------------------------------------
   Addr Operator  - contains the address of a variable - may need to add
------------------------------------------------------------------------------
   Yields the address of a variable - need to add the frame pointer if
   a variable is local to a procedure.

   Sym1<O>   Addr   Sym2<O>     meaning     Mem[Sym1<I>] := Sym2<I>
   Sym1<V>   Addr   Sym2<O>     meaning     Mem[Sym1<I>] := Sym2<I>
   Sym1<O>   Addr   Sym2<V>     meaning     Mem[Sym1<I>] := Mem[Sym2<I>]
   Sym1<V>   Addr   Sym2<V>     meaning     Mem[Sym1<I>] := Mem[Sym2<I>]
------------------------------------------------------------------------------
   Xindr Operator  ( *a = b)
------------------------------------------------------------------------------
   Sym1<O>   Copy   Sym2<I>     Meaning     Mem[Sym1<I>] := constant
   Sym1<V>   Copy   Sym2<I>     Meaning     Mem[Sym1<I>] := constant
   Sym1<O>   Copy   Sym2<O>     meaning     Mem[Sym1<I>] := Mem[Sym2<I>]
   Sym1<V>   Copy   Sym2<O>     meaning     Mem[Sym1<I>] := Mem[Sym2<I>]
   Sym1<O>   Copy   Sym2<V>     meaning     Mem[Sym1<I>] := Mem[Mem[Sym2<I>]]
   Sym1<V>   Copy   Sym2<V>     meaning     Mem[Sym1<I>] := Mem[Mem[Sym2<I>]]
------------------------------------------------------------------------------
   IndrX Operator  (a = *b)   where <X> means any value
------------------------------------------------------------------------------
   Sym1<X>   IndrX  Sym2<I>     meaning     Mem[Sym1<I>] := Mem[constant]
   Sym1<X>   IndrX  Sym2<I>     meaning     Mem[Sym1<I>] := Mem[constant]

   Sym1<X>   IndrX  Sym2<X>     meaning     Mem[Sym1<I>] := Mem[Mem[Sym2<I>]]
   Sym1<X>   IndrX  Sym2<X>     meaning     Mem[Sym1<I>] := Mem[Mem[Sym2<I>]]
------------------------------------------------------------------------------
   + - / * Operators
------------------------------------------------------------------------------
   Sym1<I>   +      Sym2<I>  Sym3<I>  meaning Sym1<I> := Sym2<I> + Sym3<I>
   Sym1<O>   +      Sym2<O>  Sym3<I>  meaning Mem[Sym1<I>] :=
                                                    Mem[Sym2<I>] + Sym3<I>
   Sym1<O>   +      Sym2<O>  Sym3<O>  meaning Mem[Sym1<I>] :=
                                                    Mem[Sym2<I>] + Mem[Sym3<I>]
   Sym1<O>   +      Sym2<O>  Sym3<V>  meaning Mem[Sym1<I>] :=
                                                    Mem[Sym2<I>] + Mem[Sym3<I>]
   Sym1<V>   +      Sym2<O>  Sym3<V>  meaning Mem[Sym1<I>] :=
                                                    Mem[Sym2<I>] + Mem[Sym3<I>]
   Sym1<V>   +      Sym2<V>  Sym3<V>  meaning Mem[Sym1<I>] :=
                                                    Mem[Sym2<I>] + Mem[Sym3<I>]
------------------------------------------------------------------------------
   Base Operator
------------------------------------------------------------------------------
   Sym1<O>   Base  Sym2   Sym3<O>     meaning     Mem[Sym1<I>] := Sym3<I>
   Sym1<V>   Base  Sym2   Sym3<O>     meaning     Should Never Occur But If it did..
                                                  Mem[Mem[Sym1<I>]] := Sym3<I>
   Sym1<O>   Base  Sym2   Sym3<V>     meaning     Mem[Sym1<I>] := Mem[Sym3<I>]
   Sym1<V>   Base  Sym2   Sym3<V>     meaning     Should Never Occur But If it did..
                                                  Mem[Mem[Sym1<I>]] := Mem[Sym3<I>]
                   Sym2 is the array type
------------------------------------------------------------------------------
*)


(*
   ErrorMessageDecl - emit an error message together with declaration fragments of left
                      and right if they are parameters or variables.
*)

PROCEDURE ErrorMessageDecl (tok: CARDINAL; message: ARRAY OF CHAR;
                            left, right: CARDINAL; iserror: BOOLEAN) ;
BEGIN
   MetaErrorT2 (tok, message, left, right) ;
   MetaErrorDecl (left, iserror) ;
   MetaErrorDecl (right, iserror)
END ErrorMessageDecl ;


(*
   IsExportedGcc - returns TRUE if this symbol should be (as far as the middle/backend of GCC)
                   is concerned, exported.
*)

PROCEDURE IsExportedGcc (sym: CARDINAL) : BOOLEAN ;
VAR
   scope: CARDINAL ;
BEGIN
   (* Has a procedure been overridden as public?  *)
   IF IsProcedure (sym) AND IsPublic (sym)
   THEN
      RETURN TRUE
   END ;
   (* Check for whole program.  *)
   IF WholeProgram
   THEN
      scope := GetScope (sym) ;
      WHILE scope # NulSym DO
         IF IsDefImp (scope)
         THEN
            RETURN IsExported (scope, sym)
         ELSIF IsModule (scope)
         THEN
            RETURN FALSE
         END ;
         scope := GetScope (scope)
      END ;
      InternalError ('expecting scope to eventually reach a module or defimp symbol')
   ELSE
      (* Otherwise it is public if it were exported.  *)
      RETURN IsExported (GetMainModule (), sym)
   END
END IsExportedGcc ;


(*
   ConvertQuadsToTree - runs through the quadruple list and converts it into
                        the GCC tree structure.
*)

PROCEDURE ConvertQuadsToTree (Start, End: CARDINAL) ;
BEGIN
   REPEAT
      CodeStatement (Start) ;
      Start := GetNextQuad (Start)
   UNTIL (Start > End) OR (Start = 0) ;
END ConvertQuadsToTree ;


(*
   IsCompilingMainModule -
*)

PROCEDURE IsCompilingMainModule (sym: CARDINAL) : BOOLEAN ;
BEGIN
   WHILE (sym # NulSym) AND (GetMainModule () # sym) DO
      sym := GetModuleScope (sym)
   END ;
   RETURN sym # NulSym
END IsCompilingMainModule ;


(*
   CodeLastForIterator - call PerformLastForIterator allowing for
                         a non constant last iterator value.
*)

PROCEDURE CodeLastForIterator (quad: CARDINAL) ;
BEGIN
   PerformLastForIterator (quad, NoWalkProcedure, FALSE)
END CodeLastForIterator ;


(*
   FoldLastForIterator - call PerformLastForIterator providing
                         all operands are constant and are known by GCC.
*)

PROCEDURE FoldLastForIterator (quad: CARDINAL; p: WalkAction) ;
VAR
   op              : QuadOperator ;
   e1, e2,
   op1, tuple, incr: CARDINAL ;
BEGIN
   GetQuad (quad, op, op1, tuple, incr) ;
   Assert (IsTuple (tuple)) ;
   e1 := GetNth (tuple, 1) ;
   e2 := GetNth (tuple, 2) ;
   IF IsConst (op1) AND IsConst (e1) AND IsConst (e2) AND IsConst (incr) AND
      GccKnowsAbout (e1) AND GccKnowsAbout (e2) AND GccKnowsAbout (incr)
   THEN
      PerformLastForIterator (quad, p, TRUE)
   END
END FoldLastForIterator ;


(*
   FoldLastForIterator - generates code to calculate the last iterator value
                         in a for loop.  It examines the increment constant
                         and generates different code depending whether it is
                         negative or positive.
*)

PROCEDURE PerformLastForIterator (quad: CARDINAL; p: WalkAction; constant: BOOLEAN) ;
VAR
   success,
   constExpr,
   overflowChecking : BOOLEAN ;
   op               : QuadOperator ;
   lastpos, op1pos,
   op2pos, incrpos,
   last, tuple, incr: CARDINAL ;
   e1, e2           : CARDINAL ;
   lasttree,
   e1tree, e2tree,
   expr, incrtree   : tree ;
   location         : location_t ;
BEGIN
   GetQuadOtok (quad, lastpos, op, last, tuple, incr,
                overflowChecking, constExpr,
                op1pos, op2pos, incrpos) ;
   DeclareConstant (incrpos, incr) ;
   lasttree := Mod2Gcc (last) ;
   success := TRUE ;
   IF IsConst (incr)
   THEN
      incrtree := Mod2Gcc (incr) ;
      location := TokenToLocation (lastpos) ;
      e1 := GetNth (tuple, 1) ;
      e2 := GetNth (tuple, 2) ;
      e1tree := Mod2Gcc (e1) ;
      e2tree := Mod2Gcc (e2) ;
      IF CompareTrees (incrtree, GetIntegerZero (location)) = 0
      THEN
         MetaErrorT0 (lastpos,
                      'the {%kFOR} loop step value must not be zero') ;
         MetaErrorDecl (incr, TRUE) ;
         NoChange := FALSE ;
         SubQuad (quad) ;
         success := FALSE
      ELSIF CompareTrees (incrtree, GetIntegerZero (location)) > 0
      THEN
         (* If incr > 0 then LastIterator := ((e2-e1) DIV incr) * incr + e1.  *)
         expr := BuildSub (location, e2tree, e1tree, FALSE) ;
         incrtree := BuildConvert (location, GetTreeType (expr), incrtree, FALSE) ;
         IF TreeOverflow (incrtree)
         THEN
            MetaErrorT0 (lastpos,
                         'the intemediate calculation for the last iterator value in the {%kFOR} loop has caused an overflow') ;
            NoChange := FALSE ;
            SubQuad (quad) ;
            success := FALSE
         ELSE
            expr := BuildDivFloor (location, expr, incrtree, FALSE) ;
            expr := BuildMult (location, expr, incrtree, FALSE) ;
            expr := BuildAdd (location, expr, e1tree, FALSE)
         END
      ELSE
         (* Else use LastIterator := e1 - ((e1-e2) DIV PositiveBy) * PositiveBy
            to avoid unsigned div signed arithmetic.  *)
         expr := BuildSub (location, e1tree, e2tree, FALSE) ;
         incrtree := BuildConvert (location, GetM2ZType (), incrtree, FALSE) ;
         incrtree := BuildNegate (location, incrtree, FALSE) ;
         incrtree := BuildConvert (location, GetTreeType (expr), incrtree, FALSE) ;
         IF TreeOverflow (incrtree)
         THEN
            MetaErrorT0 (lastpos,
                         'the intemediate calculation for the last iterator value in the {%kFOR} loop has caused an overflow') ;
            NoChange := FALSE ;
            SubQuad (quad) ;
            success := FALSE
         ELSE
            expr := BuildSub (location, e1tree, e2tree, FALSE) ;
            expr := BuildDivFloor (location, expr, incrtree, FALSE) ;
            expr := BuildMult (location, expr, incrtree, FALSE) ;
            expr := BuildSub (location, e1tree, expr, FALSE)
         END
      END ;
      IF success
      THEN
         IF IsConst (last)
         THEN
            AddModGcc (last, expr) ;
            p (last) ;
            NoChange := FALSE ;
            SubQuad (quad)
         ELSE
            Assert (NOT constant) ;
            BuildAssignmentStatement (location, lasttree, expr)
         END
      END
   ELSE
      MetaErrorT1 (lastpos,
                   'the value {%1Ead} in the {%kBY} clause of the {%kFOR} loop must be constant',
                   incr) ;
      MetaErrorDecl (incr, TRUE) ;
      NoChange := FALSE ;
      SubQuad (quad)
   END
END PerformLastForIterator ;


(*
   CodeStatement - A multi-way decision call depending on the current
                   quadruple.
*)

PROCEDURE CodeStatement (q: CARDINAL) ;
VAR
   op           : QuadOperator ;
   op1, op2, op3: CARDINAL ;
   location     : location_t ;
BEGIN
   InitBuiltinSyms (BuiltinTokenNo) ;
   GetQuad(q, op, op1, op2, op3) ;
   IF op=StatementNoteOp
   THEN
      FoldStatementNote (op3)  (* Will change CurrentQuadToken using op3.  *)
   ELSE
      CurrentQuadToken := QuadToTokenNo (q)
   END ;
   location := TokenToLocation (CurrentQuadToken) ;
   CheckReferenced(q, op) ;
   IF GetDebugTraceQuad ()
   THEN
      printf0 ('building: ') ;
      DisplayQuad (q)
   END ;

   CASE op OF

   StartDefFileOp     : CodeStartDefFile (op3) |
   StartModFileOp     : CodeStartModFile (op3) |
   ModuleScopeOp      : CodeModuleScope (op3) |
   EndFileOp          : CodeEndFile |
   InitStartOp        : CodeInitStart (op3, IsCompilingMainModule (op3)) |
   InitEndOp          : CodeInitEnd (op3, IsCompilingMainModule (op3)) |
   FinallyStartOp     : CodeFinallyStart (op3, IsCompilingMainModule (op3)) |
   FinallyEndOp       : CodeFinallyEnd (op3, IsCompilingMainModule (op3)) |
   NewLocalVarOp      : CodeNewLocalVar (op1, op3) |
   KillLocalVarOp     : CodeKillLocalVar (op3) |
   ProcedureScopeOp   : CodeProcedureScope (op3) |
   ReturnOp           : (* Not used as return is achieved by KillLocalVar.  *)  |
   ReturnValueOp      : CodeReturnValue (q) |
   TryOp              : CodeTry |
   ThrowOp            : CodeThrow (op3) |
   CatchBeginOp       : CodeCatchBegin |
   CatchEndOp         : CodeCatchEnd |
   RetryOp            : CodeRetry (op3) |
   DummyOp            : |
   InitAddressOp      : CodeInitAddress(q, op1, op2, op3) |
   BecomesOp          : CodeBecomes(q) |
   ArithAddOp,
   AddOp              : CodeAddChecked (q, op2, op3) |
   SubOp              : CodeSubChecked (q, op2, op3) |
   MultOp             : CodeMultChecked (q, op2, op3) |
   DivM2Op            : CodeDivM2Checked (q, op2, op3) |
   ModM2Op            : CodeModM2Checked (q, op2, op3) |
   DivTruncOp         : CodeDivTrunc (q, op2, op3) |
   ModTruncOp         : CodeModTrunc (q, op2, op3) |
   DivCeilOp          : CodeDivCeil (q, op2, op3) |
   ModCeilOp          : CodeModCeil (q, op2, op3) |
   DivFloorOp         : CodeDivFloor (q, op2, op3) |
   ModFloorOp         : CodeModFloor (q, op2, op3) |
   GotoOp             : CodeGoto (op3) |
   InclOp             : CodeIncl (op1, op3) |
   ExclOp             : CodeExcl (op1, op3) |
   NegateOp           : CodeNegateChecked (q, op1, op3) |
   LastForIteratorOp  : CodeLastForIterator (q) |
   LogicalShiftOp     : CodeSetShift (q, op1, op2, op3) |
   LogicalRotateOp    : CodeSetRotate (q, op1, op2, op3) |
   LogicalOrOp        : CodeSetOr (q) |
   LogicalAndOp       : CodeSetAnd (q) |
   LogicalXorOp       : CodeSetSymmetricDifference (q) |
   LogicalDiffOp      : CodeSetLogicalDifference (q) |
   IfLessOp           : CodeIfLess (q) |
   IfEquOp            : CodeIfEqu (q) |
   IfNotEquOp         : CodeIfNotEqu (q) |
   IfGreEquOp         : CodeIfGreEqu (q) |
   IfLessEquOp        : CodeIfLessEqu (q) |
   IfGreOp            : CodeIfGre (q) |
   IfInOp             : CodeIfIn (q) |
   IfNotInOp          : CodeIfNotIn (q) |
   IndrXOp            : CodeIndrX (q, op1, op2, op3) |
   XIndrOp            : CodeXIndr (q) |
   CallOp             : CodeCall (CurrentQuadToken, op3) |
   ParamOp            : CodeParam (q) |
   FunctValueOp       : CodeFunctValue (location, op1) |
   AddrOp             : CodeAddr (CurrentQuadToken, q, op1, op3) |
   SizeOp             : CodeSize (op1, op3) |
   UnboundedOp        : CodeUnbounded (op1, op3) |
   RecordFieldOp      : CodeRecordField (op1, op2, op3) |
   HighOp             : CodeHigh (op1, op2, op3) |
   ArrayOp            : CodeArray (op1, op2, op3) |
   ElementSizeOp      : InternalError ('ElementSizeOp is expected to have been folded via constant evaluation') |
   ConvertOp          : CodeConvert (q, op1, op2, op3) |
   CoerceOp           : CodeCoerce (q, op1, op2, op3) |
   CastOp             : CodeCast (q, op1, op2, op3) |
   StandardFunctionOp : CodeStandardFunction (q, op1, op2, op3) |
   SavePriorityOp     : CodeSavePriority (op1, op2, op3) |
   RestorePriorityOp  : CodeRestorePriority (op1, op2, op3) |

   InlineOp           : CodeInline (q) |
   StatementNoteOp    : CodeStatementNote (op3) |
   CodeOnOp           : |           (* The following make no sense with gcc.  *)
   CodeOffOp          : |
   ProfileOnOp        : |
   ProfileOffOp       : |
   OptimizeOnOp       : |
   OptimizeOffOp      : |
   RangeCheckOp       : CodeRange (op3) |
   ErrorOp            : CodeError (op3) |
   SaveExceptionOp    : CodeSaveException (op1, op3) |
   RestoreExceptionOp : CodeRestoreException (op1, op3)

   ELSE
      WriteFormat1 ('quadruple %d not yet implemented', q) ;
      InternalError ('quadruple not implemented yet')
   END ;
   LastOperator := op
END CodeStatement ;


(*
   ResolveConstantExpressions - resolves constant expressions from the quadruple list.
                                It returns TRUE if one or more constants were folded.
                                When a constant symbol value is solved, the call back
                                p(sym) is invoked.
*)

PROCEDURE ResolveConstantExpressions (p: WalkAction; bb: BasicBlock) : BOOLEAN ;
VAR
   tokenno: CARDINAL ;
   quad   : CARDINAL ;
   op     : QuadOperator ;
   op1,
   op2,
   op3,
   op1pos,
   op2pos,
   op3pos : CARDINAL ;
   Changed: BOOLEAN ;
   start,
   end    : CARDINAL ;
BEGIN
   InitBuiltinSyms (BuiltinTokenNo) ;
   start := GetBasicBlockStart (bb) ;
   end := GetBasicBlockEnd (bb) ;
   Changed  := FALSE ;
   REPEAT
      NoChange := TRUE ;
      quad := start ;
      WHILE (quad<=end) AND (quad#0) DO
         tokenno := CurrentQuadToken ;
         IF tokenno=0
         THEN
            tokenno := QuadToTokenNo (quad)
         END ;
         IF GetDebugTraceQuad ()
         THEN
            printf0('examining fold: ') ;
            DisplayQuad (quad)
         END ;
         GetQuadtok (quad, op, op1, op2, op3,
                     op1pos, op2pos, op3pos) ;
         CASE op OF

         StandardFunctionOp : FoldStandardFunction (tokenno, p, quad, op1, op2, op3) |
         BuiltinConstOp     : FoldBuiltinConst (tokenno, p, quad, op1, op3) |
         BuiltinTypeInfoOp  : FoldBuiltinTypeInfo (tokenno, p, quad, op1, op2, op3) |
         LogicalOrOp        : FoldSetOr (tokenno, p, quad, op1, op2, op3) |
         LogicalAndOp       : FoldSetAnd (tokenno, p, quad, op1, op2, op3) |
         LogicalXorOp       : FoldSymmetricDifference (tokenno, p, quad, op1, op2, op3) |
         BecomesOp          : FoldBecomes (p, bb, quad) |
         ArithAddOp         : FoldArithAdd (op1pos, p, quad, op1, op2, op3) |
         AddOp              : FoldAdd (op1pos, p, quad, op1, op2, op3) |
         SubOp              : FoldSub (op1pos, p, quad, op1, op2, op3) |
         MultOp             : FoldMult (op1pos, p, quad, op1, op2, op3) |
         DivM2Op            : FoldDivM2 (op1pos, p, quad, op1, op2, op3) |
         ModM2Op            : FoldModM2 (op1pos, p, quad, op1, op2, op3) |
         DivTruncOp         : FoldDivTrunc (op1pos, p, quad, op1, op2, op3) |
         ModTruncOp         : FoldModTrunc (op1pos, p, quad, op1, op2, op3) |
         DivCeilOp          : FoldDivCeil (op1pos, p, quad, op1, op2, op3) |
         ModCeilOp          : FoldModCeil (op1pos, p, quad, op1, op2, op3) |
         DivFloorOp         : FoldDivFloor (op1pos, p, quad, op1, op2, op3) |
         ModFloorOp         : FoldModFloor (op1pos, p, quad, op1, op2, op3) |
         NegateOp           : FoldNegate (op1pos, p, quad, op1, op3) |
         SizeOp             : FoldSize (tokenno, p, quad, op1, op2, op3) |
         RecordFieldOp      : FoldRecordField (tokenno, p, quad, op1, op2, op3) |
         HighOp             : FoldHigh (tokenno, p, quad, op1, op2, op3) |
         ElementSizeOp      : FoldElementSize (tokenno, p, quad, op1, op2) |
         ConvertOp          : FoldConvert (tokenno, p, quad, op1, op2, op3) |
         CoerceOp           : FoldCoerce (tokenno, p, quad, op1, op2, op3) |
         CastOp             : FoldCast (tokenno, p, quad, op1, op2, op3) |
         InclOp             : FoldIncl (tokenno, p, quad, op1, op3) |
         ExclOp             : FoldExcl (tokenno, p, quad, op1, op3) |
         IfEquOp            : FoldIfEqu (tokenno, quad, op1, op2, op3) |
         IfNotEquOp         : FoldIfNotEqu (tokenno, quad, op1, op2, op3) |
         IfLessOp           : FoldIfLess (tokenno, quad, op1, op2, op3) |
         IfLessEquOp        : FoldIfLessEqu (tokenno, quad, op1, op2, op3) |
         IfGreOp            : FoldIfGre (tokenno, quad, op1, op2, op3) |
         IfGreEquOp         : FoldIfGreEqu (tokenno, quad, op1, op2, op3) |
         IfInOp             : FoldIfIn (tokenno, quad, op1, op2, op3) |
         IfNotInOp          : FoldIfNotIn (tokenno, quad, op1, op2, op3) |
         LogicalShiftOp     : FoldSetShift(tokenno, p, quad, op1, op2, op3) |
         LogicalRotateOp    : FoldSetRotate (tokenno, p, quad, op1, op2, op3) |
         ParamOp            : FoldBuiltinFunction (tokenno, p, quad, op1, op2, op3) |
         RangeCheckOp       : FoldRange (tokenno, quad, op3) |
         StatementNoteOp    : FoldStatementNote (op3) |
         StringLengthOp      : FoldStringLength (quad, p) |
         StringConvertM2nulOp: FoldStringConvertM2nul (quad, p) |
         StringConvertCnulOp : FoldStringConvertCnul (quad, p) |
         LastForIteratorOp  : FoldLastForIterator (quad, p)

         ELSE
            (* Ignore quadruple as it is not associated with a constant expression.  *)
         END ;
         quad := GetNextQuad (quad)
      END ;
      IF NOT NoChange
      THEN
         Changed := TRUE
      END
   UNTIL NoChange ;
   RETURN Changed
END ResolveConstantExpressions ;


(*
   FindSize - given a Modula-2 symbol sym return a gcc tree
              constant representing the storage size in bytes.
*)

PROCEDURE FindSize (tokenno: CARDINAL; sym: CARDINAL) : tree ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (tokenno) ;
   IF IsConstString (sym)
   THEN
      Assert (IsConstStringKnown (sym)) ;
      PushCard (GetStringLength (tokenno, sym)) ;
      RETURN PopIntegerTree ()
   ELSIF IsSizeSolved (sym)
   THEN
      PushSize (sym) ;
      RETURN PopIntegerTree ()
   ELSE
      IF GccKnowsAbout (sym)
      THEN
         IF IsVar (sym) AND IsVariableSSA (sym)
         THEN
            sym := GetType (sym)
         END ;
         PushIntegerTree (BuildSize (location, Mod2Gcc (sym), FALSE)) ;
         PopSize (sym) ;
         PushSize (sym) ;
         RETURN PopIntegerTree ()
      ELSIF IsVar (sym) AND GccKnowsAbout (GetType (sym))
      THEN
         PushIntegerTree (BuildSize (location, Mod2Gcc (GetType (sym)), FALSE)) ;
         RETURN PopIntegerTree ()
      ELSE
         InternalError ('expecting gcc to already know about this symbol')
      END
   END
END FindSize ;


(*
   FindType - returns the type of, Sym, if Sym is a TYPE then return Sym otherwise return GetType(Sym)
*)

PROCEDURE FindType (Sym: CARDINAL) : CARDINAL ;
BEGIN
   IF IsType (Sym)
   THEN
      RETURN Sym
   ELSE
      RETURN GetType (Sym)
   END
END FindType ;


(*
   BuildTreeFromInterface - generates a GCC tree from an interface definition.
*)

PROCEDURE BuildTreeFromInterface (sym: CARDINAL) : tree ;
CONST
   DebugTokPos = FALSE ;
VAR
   tok     : CARDINAL ;
   i       : CARDINAL ;
   name    : Name ;
   str,
   obj     : CARDINAL ;
   gccName,
   asmTree : tree ;
BEGIN
   asmTree := tree (NIL) ;
   IF sym#NulSym
   THEN
      i := 1 ;
      REPEAT
         GetRegInterface (sym, i, tok, name, str, obj) ;
         IF str # NulSym
         THEN
            IF IsConstString (str)
            THEN
               DeclareConstant (tok, obj) ;
               IF name = NulName
               THEN
                  gccName := NIL
               ELSE
                  gccName := BuildCStringConstant (KeyToCharStar (name), LengthKey (name))
               END ;
               asmTree := ChainOnParamValue (asmTree, gccName, PromoteToCString (tok, str),
                                             skip_const_decl (Mod2Gcc (obj))) ;
               IF DebugTokPos
               THEN
                  WarnStringAt (InitString ('input expression'), tok)
               END
            ELSE
               MetaErrorT1 (tok,
                            'a constraint to the GNU ASM statement must be a constant string and not a {%1Ed}',
                            str)
            END
         END ;
         INC(i)
      UNTIL (str = NulSym) AND (obj = NulSym) ;
   END ;
   RETURN asmTree
END BuildTreeFromInterface ;


(*
   BuildTrashTreeFromInterface - generates a GCC string tree from an interface definition.
*)

PROCEDURE BuildTrashTreeFromInterface (sym: CARDINAL) : tree ;
CONST
   DebugTokPos = FALSE ;
VAR
   tok    : CARDINAL ;
   i      : CARDINAL ;
   str,
   obj    : CARDINAL ;
   name   : Name ;
   asmTree: tree ;
BEGIN
   asmTree := tree (NIL) ;
   IF sym # NulSym
   THEN
      i := 1 ;
      REPEAT
         GetRegInterface (sym, i, tok, name, str, obj) ;
         IF str # NulSym
         THEN
            IF IsConstString (str)
            THEN
               asmTree := AddStringToTreeList (asmTree, PromoteToCString (tok, str)) ;
               IF DebugTokPos
               THEN
                  WarnStringAt (InitString ('trash expression'), tok)
               END
            ELSE
               MetaErrorT1 (tok,
                            'a constraint to the GNU ASM statement must be a constant string and not a {%1Ed}',
                            str)
            END
         END ;
         INC (i)
      UNTIL (str = NulSym) AND (obj = NulSym)
   END ;
   RETURN asmTree
END BuildTrashTreeFromInterface ;


(*
   CodeInline - InlineOp is a quadruple which has the following format:

                InlineOp   NulSym  NulSym  Sym
*)

PROCEDURE CodeInline (quad: CARDINAL) ;
VAR
   constExpr,
   overflowChecking: BOOLEAN ;
   op              : QuadOperator ;
   op1, op2, GnuAsm: CARDINAL ;
   op1pos, op2pos,
   op3pos, asmpos  : CARDINAL ;
   string          : CARDINAL ;
   inputs,
   outputs,
   trash,
   labels          : tree ;
   location        : location_t ;
BEGIN
   GetQuadOtok (quad, asmpos, op, op1, op2, GnuAsm,
                overflowChecking, constExpr,
                op1pos, op2pos, op3pos) ;
   location := TokenToLocation (asmpos) ;
   inputs  := BuildTreeFromInterface (GetGnuAsmInput (GnuAsm)) ;
   outputs := BuildTreeFromInterface (GetGnuAsmOutput (GnuAsm)) ;
   trash   := BuildTrashTreeFromInterface (GetGnuAsmTrash (GnuAsm)) ;
   labels  := NIL ;  (* At present it makes no sence for Modula-2 to jump to a label,
                        given that labels are not allowed in Modula-2.  *)
   string  := GetGnuAsm (GnuAsm) ;
   BuildAsm (location,
             PromoteToCString (GetDeclaredMod (string), string),
             IsGnuAsmVolatile (GnuAsm), IsGnuAsmSimple (GnuAsm),
             inputs, outputs, trash, labels)
END CodeInline ;


(*
   FoldStatementNote - set CurrentQuadToken to tokennno.
*)

PROCEDURE FoldStatementNote (tokenno: CARDINAL) ;
BEGIN
   CurrentQuadToken := tokenno
END FoldStatementNote ;


(*
   CodeStatementNote - set CurrentQuadToken to tokennno and
                       add a statement note.
*)

PROCEDURE CodeStatementNote (tokenno: CARDINAL) ;
BEGIN
   IF Debugging
   THEN
      MetaErrorT0 (tokenno, '{%W} statement note')
   END ;
   CurrentQuadToken := tokenno ;
   addStmtNote (TokenToLocation (tokenno))
END CodeStatementNote ;


(*
   FoldRange - attempts to fold the range test.
               --fixme-- complete this.
*)

PROCEDURE FoldRange (tokenno: CARDINAL; (* p: WalkAction; *)
                     quad: CARDINAL; rangeno: CARDINAL) ;
BEGIN
   FoldRangeCheck (tokenno, quad, rangeno)
END FoldRange ;


(*
   CodeSaveException - op1 := op3(TRUE)
*)

PROCEDURE CodeSaveException (des, exceptionProcedure: CARDINAL) ;
VAR
   functValue: tree ;
   location  : location_t;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   BuildParam (location, Mod2Gcc (True)) ;
   BuildFunctionCallTree (location,
                          Mod2Gcc (exceptionProcedure),
                          Mod2Gcc (GetType (exceptionProcedure))) ;
   functValue := BuildFunctValue (location, Mod2Gcc (des)) ;
   AddStatement (location, functValue)
END CodeSaveException ;


(*
   CodeRestoreException - op1 := op3(op1).
*)

PROCEDURE CodeRestoreException (des, exceptionProcedure: CARDINAL) ;
VAR
   functValue: tree ;
   location  : location_t;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   BuildParam (location, Mod2Gcc (des)) ;
   BuildFunctionCallTree (location,
                          Mod2Gcc (exceptionProcedure),
                          Mod2Gcc (GetType (exceptionProcedure))) ;
   functValue := BuildFunctValue (location, Mod2Gcc (des)) ;
   AddStatement (location, functValue)
END CodeRestoreException ;


(*
   PushScope -
*)

PROCEDURE PushScope (sym: CARDINAL) ;
BEGIN
   PushWord (ScopeStack, sym)
END PushScope ;


(*
   PopScope -
*)

PROCEDURE PopScope ;
VAR
   sym: CARDINAL ;
BEGIN
   sym := PopWord (ScopeStack) ;
   Assert (sym # NulSym)
END PopScope ;


(*
   GetCurrentScopeDescription - returns a description of the current scope.
*)

PROCEDURE GetCurrentScopeDescription () : String ;
VAR
   sym : CARDINAL ;
   n   : String ;
BEGIN
   IF IsEmptyWord(ScopeStack)
   THEN
      InternalError ('not expecting scope stack to be empty')
   ELSE
      sym := PeepWord(ScopeStack, 1) ;
      n := Mark(InitStringCharStar(KeyToCharStar(GetSymName(sym)))) ;
      IF IsDefImp(sym)
      THEN
         RETURN( Sprintf1(Mark(InitString('implementation module %s')), n) )
      ELSIF IsModule(sym)
      THEN
         IF IsInnerModule(sym)
         THEN
            RETURN( Sprintf1(Mark(InitString('inner module %s')), n) )
         ELSE
            RETURN( Sprintf1(Mark(InitString('program module %s')), n) )
         END
      ELSIF IsProcedure(sym)
      THEN
         IF IsProcedureNested(sym)
         THEN
            RETURN( Sprintf1(Mark(InitString('nested procedure %s')), n) )
         ELSE
            RETURN( Sprintf1(Mark(InitString('procedure %s')), n) )
         END
      ELSE
         InternalError ('unexpected scope symbol')
      END
   END
END GetCurrentScopeDescription ;


(*
   CodeRange - encode the range test associated with op3.
*)

PROCEDURE CodeRange (rangeId: CARDINAL) ;
BEGIN
   CodeRangeCheck (rangeId, GetCurrentScopeDescription ())
END CodeRange ;


(*
   CodeError - encode the error test associated with op3.
*)

PROCEDURE CodeError (errorId: CARDINAL) ;
BEGIN
   (* We would like to test whether this position is in the same basicblock
      as any known entry point.  If so we could emit an error message.
   *)
   AddStatement (TokenToLocation (CurrentQuadToken),
                 CodeErrorCheck (errorId, GetCurrentScopeDescription (), NIL))
END CodeError ;


(*
   CodeModuleScope - ModuleScopeOp is a quadruple which has the following
                     format:

                     ModuleScopeOp  _  _  moduleSym

                     Its purpose is to reset the source file to another
                     file, hence all line numbers emitted with the
                     generated code will be relative to this source file.
*)

PROCEDURE CodeModuleScope (moduleSym: CARDINAL) ;
BEGIN
   PushScope (moduleSym)
END CodeModuleScope ;


(*
   CodeStartModFile - StartModFileOp is a quadruple which has the following
                      format:

                      StartModFileOp  _  _  moduleSym

                      A new source file has been encountered therefore
                      set LastLine to 1.
                      Call pushGlobalScope.
*)

PROCEDURE CodeStartModFile (moduleSym: CARDINAL) ;
BEGIN
   pushGlobalScope ;
   LastLine := 1 ;
   PushScope (moduleSym)
END CodeStartModFile ;


(*
   CodeStartDefFile - StartDefFileOp is a quadruple with the following
                      format:

                      StartDefFileOp  _  _  moduleSym

                      A new source file has been encountered therefore
                      set LastLine to 1.
                      Call pushGlobalScope.
*)

PROCEDURE CodeStartDefFile (moduleSym: CARDINAL) ;
BEGIN
   pushGlobalScope ;
   PushScope (moduleSym) ;
   LastLine := 1
END CodeStartDefFile ;


(*
   CodeEndFile - pops the GlobalScope.
*)

PROCEDURE CodeEndFile ;
BEGIN
   popGlobalScope
END CodeEndFile ;


(*
   CallInnerInit - produce a call to inner module initialization routine.
*)

PROCEDURE CallInnerInit (moduleSym: WORD) ;
VAR
   location             : location_t;
   ctor, init, fini, dep: CARDINAL ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
   BuildCallInner (location, Mod2Gcc (init))
END CallInnerInit ;


(*
   CallInnerFinally - produce a call to inner module finalization routine.
*)

PROCEDURE CallInnerFinally (moduleSym: WORD) ;
VAR
   location             : location_t;
   ctor, init, fini, dep: CARDINAL ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
   BuildCallInner (location, Mod2Gcc (fini))
END CallInnerFinally ;


(*
   CodeInitStart - emits starting code before the main BEGIN END of the
                   current module.
*)

PROCEDURE CodeInitStart (moduleSym: CARDINAL;
                         CompilingMainModule: BOOLEAN) ;
VAR
   location  : location_t;
   ctor, init,
   fini, dep : CARDINAL ;
BEGIN
   IF CompilingMainModule OR WholeProgram
   THEN
      location := TokenToLocation (CurrentQuadToken) ;
      GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
      BuildStartFunctionCode (location, Mod2Gcc (init),
                              IsExportedGcc (init), FALSE) ;
      ForeachInnerModuleDo (moduleSym, CallInnerInit)
   END
END CodeInitStart ;


(*
   CodeInitEnd - emits terminating code after the main BEGIN END of the
                 current module.
*)

PROCEDURE CodeInitEnd (moduleSym: CARDINAL;
                       CompilingMainModule: BOOLEAN) ;
VAR
   location  : location_t;
   ctor, init,
   fini, dep : CARDINAL ;
BEGIN
   IF CompilingMainModule OR WholeProgram
   THEN
      location := TokenToLocation (GetDeclaredMod (moduleSym)) ;
      GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
      finishFunctionDecl (location, Mod2Gcc (init)) ;
      BuildEndFunctionCode (location, Mod2Gcc (init),
                            IsModuleWithinProcedure (moduleSym))
   END
END CodeInitEnd ;


(*
   CodeFinallyStart - emits starting code before the main BEGIN END of the
                      current module.
*)

PROCEDURE CodeFinallyStart (moduleSym: CARDINAL;
                            CompilingMainModule: BOOLEAN) ;
VAR
   location  : location_t;
   ctor, init,
   fini, dep : CARDINAL ;
BEGIN
   IF CompilingMainModule OR WholeProgram
   THEN
      location := TokenToLocation (CurrentQuadToken) ;
      GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
      BuildStartFunctionCode (location, Mod2Gcc (fini),
                              IsExportedGcc (fini), FALSE) ;
      ForeachInnerModuleDo (moduleSym, CallInnerFinally)
   END
END CodeFinallyStart ;


(*
   CodeFinallyEnd - emits terminating code after the main BEGIN END of the
                    current module.  It also creates the scaffold if the
                    cflag was not present.
*)

PROCEDURE CodeFinallyEnd (moduleSym: CARDINAL;
                          CompilingMainModule: BOOLEAN) ;
VAR
   location  : location_t;
   tokenpos  : CARDINAL ;
   ctor, init,
   fini, dep : CARDINAL ;
BEGIN
   IF CompilingMainModule OR WholeProgram
   THEN
      tokenpos := GetDeclaredMod (moduleSym) ;
      location := TokenToLocation (tokenpos) ;
      GetModuleCtors (moduleSym, ctor, init, fini, dep) ;
      finishFunctionDecl (location, Mod2Gcc (fini)) ;
      BuildEndFunctionCode (location, Mod2Gcc (fini),
                            IsModuleWithinProcedure (moduleSym))
   END
END CodeFinallyEnd ;


(*
   GetAddressOfUnbounded - returns the address of the unbounded array contents.
*)

PROCEDURE GetAddressOfUnbounded (location: location_t; param: CARDINAL) : tree ;
VAR
   UnboundedType: CARDINAL ;
BEGIN
   UnboundedType := GetType (param) ;
   Assert (IsUnbounded (UnboundedType)) ;

   RETURN BuildConvert (TokenToLocation (GetDeclaredMod (param)),
                        GetPointerType (),
                        BuildComponentRef (location, Mod2Gcc (param), Mod2Gcc (GetUnboundedAddressOffset (UnboundedType))),
                        FALSE)
END GetAddressOfUnbounded ;


(*
   GetHighFromUnbounded - returns a Tree containing the value of
                          param.HIGH.
*)

PROCEDURE GetHighFromUnbounded (location: location_t; dim, param: CARDINAL) : tree ;
VAR
   UnboundedType,
   ArrayType,
   HighField    : CARDINAL ;
   HighTree     : tree ;
   accessibleDim: CARDINAL ;
   (* remainingDim : CARDINAL ;  *)
BEGIN
   UnboundedType := GetType (param) ;
   Assert (IsUnbounded (UnboundedType)) ;
   ArrayType := GetType (UnboundedType) ;
   HighField := GetUnboundedHighOffset (UnboundedType, dim) ;
   IF HighField = NulSym
   THEN
      (* It might be a dynamic array of static arrays,
         so lets see if there is an earlier dimension available.  *)
      accessibleDim := dim ;
      WHILE (HighField = NulSym) AND (accessibleDim > 1) DO
         DEC (accessibleDim) ;
         HighField := GetUnboundedHighOffset(UnboundedType, accessibleDim)
      END ;
      IF HighField = NulSym
      THEN
         MetaError1 ('{%EkHIGH} dimension number {%1N} for array does not exist', dim) ;
         RETURN GetCardinalZero (location)
      ELSE
         (* remainingDim := dim - accessibleDim ;  --fixme-- write tests to stress this code.  *)
         HighTree := BuildHighFromStaticArray (location, (* remainingDim, *) ArrayType) ;
         IF HighTree = NIL
         THEN
            MetaError1 ('{%EkHIGH} dimension number {%1N} for array does not exist', dim) ;
            RETURN GetCardinalZero (location)
         END ;
         RETURN HighTree
      END
   ELSE
      RETURN BuildComponentRef (location, Mod2Gcc (param), Mod2Gcc (HighField))
   END
END GetHighFromUnbounded ;


(*
   GetSizeOfHighFromUnbounded - returns a Tree containing the value of
                                param.HIGH * sizeof(unboundedType).
                                The number of legal bytes this array
                                occupies.
*)

PROCEDURE GetSizeOfHighFromUnbounded (tokenno: CARDINAL; param: CARDINAL) : tree ;
VAR
   t            : tree ;
   UnboundedType,
   ArrayType    : CARDINAL ;
   i, n         : CARDINAL ;
   location     : location_t;
BEGIN
   location := TokenToLocation(tokenno) ;
   UnboundedType := GetType(param) ;
   Assert(IsUnbounded(UnboundedType)) ;
   ArrayType := GetType(UnboundedType) ;

   i := 1 ;
   n := GetDimension(UnboundedType) ;
   t := GetCardinalOne(location) ;
   WHILE i<=n DO
      t := BuildMult(location,
                     BuildAdd(location,
                              GetHighFromUnbounded(location, i, param),
                              GetCardinalOne(location),
                              FALSE),
                     t, FALSE) ;
      (* Remember we must add one as a[HIGH(a)] is the last accessible element of the array.  *)
      INC(i)
   END ;
   RETURN( BuildConvert(location,
                        GetCardinalType(),
                        BuildMult(location,
                                  t, BuildConvert(location,
                                                  GetCardinalType(),
                                                  FindSize(tokenno, ArrayType), FALSE), FALSE),
                        FALSE) )
END GetSizeOfHighFromUnbounded ;


(*
   MaybeDebugBuiltinAlloca - if DebugBuiltins is set
                             then call Builtins.alloca_trace
                             else call Builtins.alloca.
*)

PROCEDURE MaybeDebugBuiltinAlloca (location: location_t; tok: CARDINAL; high: tree) : tree ;
VAR
   call,
   memptr,
   func  : tree ;
BEGIN
   IF DebugBuiltins
   THEN
      func := Mod2Gcc (FromModuleGetSym (tok,
                                         MakeKey ('alloca_trace'),
                                         MakeDefinitionSource (tok,
                                                               MakeKey ('Builtins')))) ;
      call := BuiltInAlloca (location, high) ;
      SetLastFunction (call) ;
      memptr := BuildFunctValue (location, call) ;
      call := BuildCall2 (location, func, GetPointerType(), memptr, high) ;
   ELSE
      call := BuiltInAlloca (location, high)
   END ;
   SetLastFunction (call) ;
   RETURN BuildFunctValue (location, call)
END MaybeDebugBuiltinAlloca ;


(*
   MaybeDebugBuiltinMemcpy - if DebugBuiltins is set
                             then call memcpy
                             else call Builtins.memcpy.
*)

PROCEDURE MaybeDebugBuiltinMemcpy (location: location_t; src, dest, nbytes: tree) : tree ;
VAR
   call,
   func: tree ;
BEGIN
   IF DebugBuiltins
   THEN
      func := Mod2Gcc (Memcpy) ;
      call := BuildCall3 (location, func, GetPointerType (), src, dest, nbytes) ;
   ELSE
      call := BuiltinMemCopy (location, src, dest, nbytes)
   END ;
   SetLastFunction (call) ;
   RETURN BuildFunctValue (location, call)
END MaybeDebugBuiltinMemcpy ;


(*
   MakeCopyUse - make a copy of the unbounded array and alter all references
                 from the old unbounded array to the new unbounded array.
                 The parameter, param, contains a RECORD
                                                     ArrayAddress: ADDRESS ;
                                                     ArrayHigh   : CARDINAL ;
                                                  END
                 we simply declare a new array of size, ArrayHigh
                 and set ArrayAddress to the address of the copy.

                 Remember ArrayHigh == sizeof(Array)-sizeof(typeof(array))
                          so we add 1 for the size and add 1 for a possible <nul>
*)

PROCEDURE MakeCopyUse (tokenno: CARDINAL; param: CARDINAL) ;
VAR
   location     : location_t;
   UnboundedType: CARDINAL ;
   Addr,
   High,
   NewArray     : tree ;
BEGIN
   location := TokenToLocation (tokenno) ;
   UnboundedType := GetType (param) ;
   Assert (IsUnbounded (UnboundedType)) ;

   High := GetSizeOfHighFromUnbounded (tokenno, param) ;
   Addr := GetAddressOfUnbounded (location, param) ;

   NewArray := MaybeDebugBuiltinAlloca (location, tokenno, High) ;
   NewArray := MaybeDebugBuiltinMemcpy (location, NewArray, Addr, High) ;

   (* Now assign  param.Addr := ADR(NewArray).  *)

   BuildAssignmentStatement (location,
                             BuildComponentRef (location,
                                                Mod2Gcc (param),
                                                Mod2Gcc (GetUnboundedAddressOffset (UnboundedType))),
                             NewArray)
END MakeCopyUse ;


(*
   GetParamAddress - returns the address of parameter, param.
*)

PROCEDURE GetParamAddress (location: location_t; proc, param: CARDINAL) : tree ;
VAR
   sym,
   type: CARDINAL ;
BEGIN
   IF IsParameter (param)
   THEN
      type := GetType (param) ;
      sym := GetLocalSym (proc, GetSymName (param)) ;
      IF IsUnbounded (type)
      THEN
         RETURN( GetAddressOfUnbounded (location, sym) )
      ELSE
         Assert (GetMode (sym) = LeftValue) ;
         RETURN( Mod2Gcc (sym) )
      END
   ELSE
      Assert (IsVar (param)) ;
      Assert (GetMode (param) = LeftValue) ;
      RETURN( Mod2Gcc(param) )
   END
END GetParamAddress ;


(*
   IsUnboundedWrittenTo - returns TRUE if the unbounded parameter
                          might be written to, or if -funbounded-by-reference
                          was _not_ specified.
*)

PROCEDURE IsUnboundedWrittenTo (proc, param: CARDINAL) : BOOLEAN ;
VAR
   f     : String ;
   l     : CARDINAL ;
   sym   : CARDINAL ;
   n1, n2: Name ;
BEGIN
   sym := GetLocalSym(proc, GetSymName(param)) ;
   IF sym=NulSym
   THEN
      InternalError ('should find symbol in table')
   ELSE
      IF UnboundedByReference
      THEN
         IF (NOT GetVarWritten(sym)) AND VerboseUnbounded
         THEN
            n1 := GetSymName(sym) ;
            n2 := GetSymName(proc) ;
            f := FindFileNameFromToken(GetDeclaredMod(sym), 0) ;
            l := TokenToLineNo(GetDeclaredMod(sym), 0) ;
            printf4('%s:%d:non VAR unbounded parameter %a in procedure %a does not need to be copied\n',
                    f, l, n1, n2)
         END ;
         RETURN( GetVarWritten(sym) )
      ELSE
         RETURN( TRUE )
      END
   END
END IsUnboundedWrittenTo ;


(*
   GetParamSize - returns the size in bytes of, param.
*)

PROCEDURE GetParamSize (tokenno: CARDINAL; param: CARDINAL) : tree ;
BEGIN
   Assert(IsVar(param) OR IsParameter(param)) ;
   IF IsUnbounded(param)
   THEN
      RETURN GetSizeOfHighFromUnbounded(tokenno, param)
   ELSE
      RETURN BuildSize (TokenToLocation (tokenno), Mod2Gcc (GetType (param)), FALSE)
   END
END GetParamSize ;


(*
   DoIsIntersection - jumps to, tLabel, if the ranges i1..i2  j1..j2 overlap
                      else jump to, fLabel.
*)

PROCEDURE DoIsIntersection (tokenno: CARDINAL; ta, tb, tc, td: tree; tLabel, fLabel: String) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   (*
     if (ta>td) OR (tb<tc)
     then
        goto fLabel
     else
        goto tLabel
     fi
   *)
   DoJump(location, BuildGreaterThan(location, ta, td), NIL, string(fLabel)) ;
   DoJump(location, BuildLessThan(location, tb, tc), NIL, string(fLabel)) ;
   BuildGoto(location, string(tLabel)) ;
   IF CascadedDebugging
   THEN
      printf1('label used %s\n', tLabel) ;
      printf1('label used %s\n', fLabel)
   END
END DoIsIntersection ;


(*
   BuildCascadedIfThenElsif - mustCheck contains a list of variables which
                              must be checked against the address of (proc, param, i).
                              If the address matches we make a copy of the unbounded
                              parameter (proc, param) and quit further checking.
*)

PROCEDURE BuildCascadedIfThenElsif (tokenno: CARDINAL;
                                    mustCheck: List;
                                    proc, param: CARDINAL) ;
VAR
   ta, tb,
   tc, td  : tree ;
   n, j    : CARDINAL ;
   tLabel,
   fLabel,
   nLabel  : String ;
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   n := NoOfItemsInList(mustCheck) ;
   (* We want a sequence of if then elsif statements.  *)
   IF n>0
   THEN
      INC(UnboundedLabelNo) ;
      j := 1 ;
      ta := GetAddressOfUnbounded(location, param) ;
      tb := BuildConvert(TokenToLocation(tokenno),
                         GetPointerType(),
                         BuildAddAddress(location, ta, GetSizeOfHighFromUnbounded(tokenno, param)),
                         FALSE) ;
      WHILE j<=n DO
         IF j>1
         THEN
            nLabel := CreateLabelProcedureN(proc, "n", UnboundedLabelNo, j) ;
            IF CascadedDebugging
            THEN
               printf1('label declared %s\n', nLabel)
            END ;
            DeclareLabel(location, string(nLabel)) ;
         END ;
         tc := GetParamAddress(location, proc, GetItemFromList(mustCheck, j)) ;
         td := BuildConvert(TokenToLocation(tokenno),
                            GetPointerType(),
                            BuildAddAddress(location, tc, GetParamSize(tokenno, param)),
                            FALSE) ;
         tLabel := CreateLabelProcedureN(proc, "t", UnboundedLabelNo, j+1) ;
         fLabel := CreateLabelProcedureN(proc, "f", UnboundedLabelNo, j+1) ;
         DoIsIntersection(tokenno, ta, tb, tc, td, tLabel, fLabel) ;
         IF CascadedDebugging
         THEN
            printf1('label declared %s\n', tLabel)
         END ;
         DeclareLabel (location, string (tLabel)) ;
         MakeCopyUse (tokenno, param) ;
         IF j<n
         THEN
            nLabel := CreateLabelProcedureN(proc, "n", UnboundedLabelNo, n+1) ;
            BuildGoto(location, string(nLabel)) ;
            IF CascadedDebugging
            THEN
               printf1('goto %s\n', nLabel)
            END
         END ;
         IF CascadedDebugging
         THEN
            printf1('label declared %s\n', fLabel)
         END ;
         DeclareLabel(location, string(fLabel)) ;
         INC(j)
      END ;
(*
      nLabel := CreateLabelProcedureN(proc, "fin", UnboundedLabelNo, n+1) ;
      IF CascadedDebugging
      THEN
         printf1('label declared %s\n', nLabel)
      END ;
      DeclareLabel(location, string(nLabel))
*)
   END
END BuildCascadedIfThenElsif ;


(*
   CheckUnboundedNonVarParameter - if non var unbounded parameter is written to
                                   then
                                      make a copy of the contents of this parameter
                                      and use the copy
                                   else if param
                                      is type compatible with any parameter, symv
                                      and at runtime its address matches symv
                                   then
                                      make a copy of the contents of this parameter
                                      and use the copy
                                   fi
*)

PROCEDURE CheckUnboundedNonVarParameter (tokenno: CARDINAL;
                                         trashed: List;
                                         proc, param: CARDINAL) ;
VAR
   mustCheck   : List ;
   paramTrashed,
   n, j        : CARDINAL ;
   f           : String ;
   l           : CARDINAL ;
   n1, n2      : Name ;
BEGIN
   IF IsUnboundedWrittenTo(proc, param)
   THEN
      MakeCopyUse (tokenno, param)
   ELSE
      InitList(mustCheck) ;
      n := NoOfItemsInList(trashed) ;
      j := 1 ;
      WHILE j<=n DO
         paramTrashed := GetItemFromList(trashed, j) ;
         IF IsAssignmentCompatible(GetLowestType(param), GetLowestType(paramTrashed))
         THEN
            (* We must check whether this unbounded parameter has the same
               address as the trashed parameter.  *)
            IF VerboseUnbounded
            THEN
               n1 := GetSymName(paramTrashed) ;
               n2 := GetSymName(proc) ;
               f := FindFileNameFromToken(GetDeclaredMod(paramTrashed), 0) ;
               l := TokenToLineNo(GetDeclaredMod(paramTrashed), 0) ;
               printf4('%s:%d:must check at runtime the address of parameter, %a, in procedure, %a, whose contents will be trashed\n',
                       f, l, n1, n2) ;
               n1 := GetSymName(param) ;
               n2 := GetSymName(paramTrashed) ;
               printf4('%s:%d:against address of parameter, %a, possibly resulting in a copy of parameter, %a\n',
                       f, l, n1, n2)
            END ;
            PutItemIntoList(mustCheck, paramTrashed)
         END ;
         INC(j)
      END ;
      (* Now we build a sequence of if then { elsif then } end to check addresses.  *)
      BuildCascadedIfThenElsif (tokenno, mustCheck, proc, param) ;
      KillList(mustCheck)
   END
END CheckUnboundedNonVarParameter ;


(*
   IsParameterWritten - returns TRUE if a parameter, sym, is written to.
*)

PROCEDURE IsParameterWritten (proc: CARDINAL; sym: CARDINAL) : BOOLEAN ;
BEGIN
   IF IsParameter(sym)
   THEN
      sym := GetLocalSym(proc, GetSymName(sym))
   END ;
   IF IsVar(sym)
   THEN
      (* Unbounded arrays will appear as vars.  *)
      RETURN GetVarWritten(sym)
   END ;
   InternalError ('expecting IsVar to return TRUE')
END IsParameterWritten ;


(*
   SaveNonVarUnboundedParameters - for each var parameter, symv, do
                                      (* not just unbounded var parameters, but _all_
                                         parameters *)
                                      if symv is written to
                                      then
                                         add symv to a compile list
                                      fi
                                   done

                                   for each parameter of procedure, symu, do
                                      if non var unbounded parameter is written to
                                      then
                                         make a copy of the contents of this parameter
                                         and use the copy
                                      else if
                                         symu is type compatible with any parameter, symv
                                         and at runtime its address matches symv
                                      then
                                         make a copy of the contents of this parameter
                                         and use the copy
                                      fi
                                   done
*)

PROCEDURE SaveNonVarUnboundedParameters (tokenno: CARDINAL; proc: CARDINAL) ;
VAR
   i, p   : CARDINAL ;
   trashed: List ;
   f      : String ;
   sym    : CARDINAL ;
   l      : CARDINAL ;
   n1, n2 : Name ;
BEGIN
   InitList(trashed) ;
   i := 1 ;
   p := NoOfParamAny (proc) ;
   WHILE i<=p DO
      sym := GetNthParamAny (proc, i) ;
      IF IsParameterWritten(proc, sym)
      THEN
         IF VerboseUnbounded
         THEN
            n1 := GetSymName(sym) ;
            n2 := GetSymName(proc) ;
            f := FindFileNameFromToken(GetDeclaredMod(sym), 0) ;
            l := TokenToLineNo(GetDeclaredMod(sym), 0) ;
            printf4('%s:%d:parameter, %a, in procedure, %a, is trashed\n',
                    f, l, n1, n2)
         END ;
         PutItemIntoList(trashed, sym)
      END ;
      INC(i)
   END ;
   (* Now see whether we need to copy any unbounded array parameters.  *)
   i := 1 ;
   p := NoOfParamAny (proc) ;
   WHILE i<=p DO
      IF IsUnboundedParamAny (proc, i) AND (NOT IsVarParamAny (proc, i))
      THEN
         CheckUnboundedNonVarParameter (tokenno, trashed, proc, GetNth (proc, i))
      END ;
      INC(i)
   END ;
   KillList(trashed)
END SaveNonVarUnboundedParameters ;


(*
   AutoInitVariable -
*)

PROCEDURE AutoInitVariable (location: location_t; sym: CARDINAL) ;
VAR
   type: CARDINAL ;
BEGIN
   IF (NOT IsParameter (sym)) AND IsVar (sym) AND
      (NOT IsTemporary (sym))
   THEN
      (* PrintSym (sym) ; *)
      type := SkipType (GetType (sym)) ;
      (* The type SYSTEM.ADDRESS is a pointer type.  *)
      IF IsPointer (type)
      THEN
         BuildAssignmentStatement (location,
                                   Mod2Gcc (sym),
                                   BuildConvert (location,
                                                 Mod2Gcc (GetType (sym)),
                                                 GetPointerZero (location),
                                                 TRUE))
      END
   END
END AutoInitVariable ;


(*
   AutoInitialize - scope will be a procedure, module or defimp.  All pointer
                    variables are assigned to NIL.
*)

PROCEDURE AutoInitialize (location: location_t; scope: CARDINAL) ;
VAR
   i, n: CARDINAL ;
BEGIN
   IF AutoInit
   THEN
      n := NoOfVariables (scope) ;
      i := 1 ;
      IF IsProcedure (scope)
      THEN
         (* The parameters are stored as local variables.  *)
         INC (i, NoOfParamAny (scope))
      END ;
      WHILE i <= n DO
         AutoInitVariable (location, GetNth (scope, i)) ;
         INC (i)
      END
   END
END AutoInitialize ;


(*
   CodeNewLocalVar - Builds a new frame on the stack to contain the procedure
                     local variables.
*)

PROCEDURE CodeNewLocalVar (tokenno, CurrentProcedure: CARDINAL) ;
VAR
   begin, end: CARDINAL ;
BEGIN
   (* Callee saves non var unbounded parameter contents.  *)
   SaveNonVarUnboundedParameters (tokenno, CurrentProcedure) ;
   BuildPushFunctionContext ;
   GetProcedureBeginEnd (CurrentProcedure, begin, end) ;
   CurrentQuadToken := begin ;
   SetBeginLocation (TokenToLocation (begin)) ;
   AutoInitialize (TokenToLocation (begin), CurrentProcedure) ;
   ForeachProcedureDo (CurrentProcedure, CodeBlock) ;
   ForeachInnerModuleDo (CurrentProcedure, CodeBlock) ;
   BuildPopFunctionContext ;
   ForeachInnerModuleDo (CurrentProcedure, CallInnerInit)
END CodeNewLocalVar ;


(*
   CodeKillLocalVar - removes local variables and returns to previous scope.
*)

PROCEDURE CodeKillLocalVar (CurrentProcedure: CARDINAL) ;
VAR
   begin, end: CARDINAL ;
   proc      : tree ;
BEGIN
   GetProcedureBeginEnd (CurrentProcedure, begin, end) ;
   CurrentQuadToken := end ;
   proc := NIL ;
   IF IsCtor (CurrentProcedure)
   THEN
      proc := DeclareModuleCtor (Mod2Gcc (CurrentProcedure))
   END ;
   BuildEndFunctionCode (TokenToLocation (end),
                         Mod2Gcc (CurrentProcedure),
                         IsProcedureGccNested (CurrentProcedure)) ;
   IF IsCtor (CurrentProcedure) AND (proc # NIL)
   THEN
      BuildModuleCtor (proc)
   END ;
   PoisonSymbols (CurrentProcedure) ;
   removeStmtNote () ;
   PopScope
END CodeKillLocalVar ;


(*
   CodeProcedureScope - start a procedure scope for CurrentProcedure.
*)

PROCEDURE CodeProcedureScope (CurrentProcedure: CARDINAL) ;
VAR
   begin, end: CARDINAL ;
BEGIN
   removeStmtNote () ;
   GetProcedureBeginEnd (CurrentProcedure, begin, end) ;
   BuildStartFunctionCode (TokenToLocation (begin),
                           Mod2Gcc (CurrentProcedure),
                           IsExportedGcc (CurrentProcedure),
                           IsProcedureInline (CurrentProcedure)) ;
   StartDeclareScope (CurrentProcedure) ;
   PushScope (CurrentProcedure) ;
   (* DeclareParameters(CurrentProcedure) *)
END CodeProcedureScope ;


(*
   CodeReturnValue - places the operand into the return value space
                     allocated by the function call.
*)

PROCEDURE CodeReturnValue (quad: CARDINAL) ;
VAR
   op                                  : QuadOperator ;
   constExpr,
   overflowChecking                    : BOOLEAN ;
   expr, none, procedure               : CARDINAL ;
   combinedpos,
   returnpos, exprpos, nonepos, procpos: CARDINAL ;
   value, length                       : tree ;
   location                            : location_t ;
BEGIN
   GetQuadOtok (quad, returnpos, op, expr, none, procedure,
                overflowChecking, constExpr,
                exprpos, nonepos, procpos) ;
   combinedpos := MakeVirtualTok (returnpos, returnpos, exprpos) ;
   location := TokenToLocation (combinedpos) ;
   TryDeclareConstant (exprpos, expr) ;  (* Checks to see whether it is a constant and declares it.  *)
   TryDeclareConstructor (exprpos, expr) ;
   IF IsConstString (expr) AND (SkipTypeAndSubrange (GetType (procedure)) # Char)
   THEN
      IF NOT PrepareCopyString (returnpos, length, value, expr, GetType (procedure))
      THEN
         MetaErrorT3 (MakeVirtualTok (returnpos, returnpos, exprpos),
                      'string constant {%1Ea} is too large to be returned from procedure {%2a} via the {%3d} {%3a}',
                      expr, procedure, GetType (procedure))
      END ;
      value := BuildArrayStringConstructor (location,
                                            Mod2Gcc (GetType (procedure)),
                                            value, length)
   ELSE
      value := Mod2Gcc (expr)
   END ;
   BuildReturnValueCode (location, Mod2Gcc (procedure), value)
END CodeReturnValue ;


(*
   CodeCall - determines whether the procedure call is a direct call
              or an indirect procedure call.
*)

PROCEDURE CodeCall (tokenno: CARDINAL; procedure: CARDINAL) ;
VAR
   callTree: tree ;
   location: location_t ;
BEGIN
   IF IsProcedure (procedure)
   THEN
      DeclareParameters (procedure) ;
      callTree := CodeDirectCall (tokenno, procedure)
   ELSIF IsProcType (SkipType (GetType (procedure)))
   THEN
      DeclareParameters (SkipType (GetType (procedure))) ;
      callTree := CodeIndirectCall (tokenno, procedure) ;
      procedure := SkipType (GetType (procedure))
   ELSE
      InternalError ('expecting Procedure or ProcType')
   END ;
   IF GetType (procedure) = NulSym
   THEN
      location := TokenToLocation (tokenno) ;
      AddStatement (location, callTree)
   ELSE
      (* Leave tree alone - as it will be picked up when processing FunctValue.  *)
   END
END CodeCall ;


(*
   UseBuiltin - returns a Tree containing the builtin function
                and parameters. It should only be called if
                CanUseBuiltin or IsProcedureBuiltinAvailable returns TRUE.
*)

PROCEDURE UseBuiltin (tokenno: CARDINAL; Sym: CARDINAL) : tree ;
BEGIN
   IF BuiltinExists(KeyToCharStar(GetProcedureBuiltin(Sym)))
   THEN
      RETURN( BuildBuiltinTree(TokenToLocation (tokenno), KeyToCharStar (GetProcedureBuiltin (Sym))) )
   ELSE
      RETURN( BuildBuiltinTree(TokenToLocation (tokenno), KeyToCharStar (GetSymName (Sym))) )
   END
END UseBuiltin ;


(*
   CodeDirectCall - calls a function/procedure.
*)

PROCEDURE CodeDirectCall (tokenno: CARDINAL; procedure: CARDINAL) : tree ;
VAR
   location: location_t ;
   call    : tree ;
BEGIN
   location := TokenToLocation (tokenno) ;
   IF IsProcedureBuiltinAvailable (procedure)
   THEN
      call := UseBuiltin (tokenno, procedure) ;
      IF call # NIL
      THEN
         call := BuildBuiltinCallTree (call)
      END
   ELSE
      call := NIL
   END ;
   IF call = NIL
   THEN
      IF GetType (procedure) = NulSym
      THEN
         call := BuildProcedureCallTree (location, Mod2Gcc (procedure), NIL)
      ELSE
         call := BuildProcedureCallTree (location, Mod2Gcc (procedure), Mod2Gcc (GetType (procedure)))
      END
   END ;
   IF GetType (procedure) = NulSym
   THEN
      SetLastFunction (NIL)
   ELSE
      SetLastFunction (call)
   END ;
   RETURN call
END CodeDirectCall ;


(*
   CodeIndirectCall - calls a function/procedure indirectly.
*)

PROCEDURE CodeIndirectCall (tokenno: CARDINAL; ProcVar: CARDINAL) : tree ;
VAR
   ReturnType: tree ;
   proc      : CARDINAL ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   proc := SkipType(GetType(ProcVar)) ;
   IF GetType(proc)=NulSym
   THEN
      ReturnType := tree(NIL)
   ELSE
      ReturnType := tree(Mod2Gcc(GetType(proc)))
   END ;

   (* Now we dereference the lvalue if necessary.  *)

   IF GetMode(ProcVar)=LeftValue
   THEN
      RETURN BuildIndirectProcedureCallTree(location,
                                             BuildIndirect(location, Mod2Gcc(ProcVar), Mod2Gcc(proc)),
                                             ReturnType)
   ELSE
      RETURN BuildIndirectProcedureCallTree(location, Mod2Gcc(ProcVar), ReturnType)
   END
END CodeIndirectCall ;


(*
   StringToChar - if type=Char and str is a string (of size <= 1)
                  then convert the string into a character constant.
*)

PROCEDURE StringToChar (t: tree; type, str: CARDINAL) : tree ;
VAR
   s: String ;
   n: Name ;
   tokenno : CARDINAL ;
   location: location_t ;
BEGIN
   tokenno := GetDeclaredMod(str) ;
   location := TokenToLocation(tokenno) ;
   type := SkipType (type) ;
   IF (type=Char) AND IsConstString(str)
   THEN
      Assert (IsConstStringKnown (str)) ;
      IF GetStringLength (tokenno, str) = 0
      THEN
         s := InitString('') ;
         t := BuildCharConstant(location, s) ;
         s := KillString(s) ;
      ELSIF GetStringLength (tokenno, str)>1
      THEN
         n := GetSymName(str) ;
         WriteFormat1("type incompatibility, attempting to use a string ('%a') when a CHAR is expected", n) ;
         s := InitString('') ;  (* Do something safe.  *)
         t := BuildCharConstant(location, s)
      END ;
      s := InitStringCharStar(KeyToCharStar(GetString(str))) ;
      s := Slice(s, 0, 1) ;
      t := BuildCharConstant(location, string(s)) ;
      s := KillString(s) ;
   END ;
   RETURN( t )
END StringToChar ;


(*
   ConvertTo - convert gcc tree, t, (which currently represents Modula-2 op3) into
               a symbol of, type.
*)

PROCEDURE ConvertTo (t: tree; type, op3: CARDINAL) : tree ;
BEGIN
   IF SkipType(type)#SkipType(GetType(op3))
   THEN
      IF IsConst(op3) AND (NOT IsConstString(op3))
      THEN
         PushValue(op3) ;
         RETURN( BuildConvert(TokenToLocation(GetDeclaredMod(op3)),
                              Mod2Gcc(type), t, FALSE) )
      END
   END ;
   RETURN( t )
END ConvertTo ;


(*
   ConvertRHS - convert (t, rhs) into, type.  (t, rhs) refer to the
                same entity t is a GCC Tree and, rhs, is a Modula-2
                symbol.  It checks for char and strings
                first and then the remaining types.
*)

PROCEDURE ConvertRHS (t: tree; type, rhs: CARDINAL) : tree ;
BEGIN
   t := StringToChar (Mod2Gcc (rhs), type, rhs) ;
   RETURN ConvertTo (t, type, rhs)
END ConvertRHS ;


(*
   IsCoerceableParameter - returns TRUE if symbol, sym, is a
                           coerceable parameter.
*)

PROCEDURE IsCoerceableParameter (sym: CARDINAL) : BOOLEAN ;
BEGIN
   RETURN(
          IsSet(sym) OR
          (IsOrdinalType(sym) AND (sym#Boolean) AND (NOT IsEnumeration(sym))) OR
          IsComplexType(sym) OR IsRealType(sym) OR
          IsComplexN(sym) OR IsRealN(sym) OR IsSetN(sym)
         )
END IsCoerceableParameter ;


(*
   IsConstProcedure - returns TRUE if, p, is a const procedure.
*)

PROCEDURE IsConstProcedure (p: CARDINAL) : BOOLEAN ;
BEGIN
   RETURN( IsConst(p) AND (GetType(p)#NulSym) AND IsProcType(GetType(p)) )
END IsConstProcedure ;


(*
   IsConstant - returns TRUE if symbol, p, is either a const or procedure.
*)

PROCEDURE IsConstant (p: CARDINAL) : BOOLEAN ;
BEGIN
   RETURN IsConst (p) OR IsProcedure (p)
END IsConstant ;


(*
   CheckConvertCoerceParameter - ensure that actual parameter is the same as the nth of callee.
*)

PROCEDURE CheckConvertCoerceParameter (tokenno: CARDINAL; nth, callee, actual: CARDINAL) : tree ;
VAR
   OperandType,
   ParamType  : CARDINAL ;
   location   : location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   IF GetNthParamAny (callee, nth)=NulSym
   THEN
      (* We reach here if the argument is being passed to a C vararg function.  *)
      RETURN( Mod2Gcc(actual) )
   ELSE
      OperandType := SkipType(GetType(actual)) ;
      ParamType := SkipType(GetType(GetNthParamAny (callee, nth)))
   END ;
   IF IsProcType(ParamType)
   THEN
      IF IsProcedure(actual) OR IsConstProcedure(actual) OR (OperandType = ParamType)
      THEN
         RETURN( Mod2Gcc(actual) )
      ELSE
         RETURN( BuildConvert(location, Mod2Gcc(ParamType), Mod2Gcc(actual), FALSE) )
      END
   ELSIF IsRealType(OperandType) AND IsRealType(ParamType) AND
      (ParamType#OperandType)
   THEN
      (* SHORTREAL, LONGREAL and REAL conversion during parameter passing.  *)
      RETURN( BuildConvert(location, Mod2Gcc(ParamType),
                           Mod2Gcc(actual), FALSE) )
   ELSIF (OperandType#NulSym) AND IsSet(OperandType) AND IsConst(actual)
   THEN
      RETURN( DeclareKnownConstant(location,
                                   Mod2Gcc(ParamType),
                                   Mod2Gcc(actual)) )
   ELSIF IsConst(actual) AND
         (IsOrdinalType(ParamType) OR IsSystemType(ParamType))
   THEN
      RETURN( BuildConvert(location, Mod2Gcc(ParamType),
                           StringToChar(Mod2Gcc(actual), ParamType, actual),
                           FALSE) )
   ELSIF IsConstString(actual) OR ((OperandType#NulSym) AND IsCoerceableParameter(OperandType) AND (OperandType#ParamType))
   THEN
      RETURN( BuildConvert(location, Mod2Gcc(ParamType), Mod2Gcc(actual), FALSE) )
   ELSE
      RETURN( Mod2Gcc(actual) )
   END
END CheckConvertCoerceParameter ;


(*
   CheckConstant - checks to see whether we should declare the constant.
*)

PROCEDURE CheckConstant (tokenno: CARDINAL; des, expr: CARDINAL) : tree ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   IF IsProcedure(expr)
   THEN
      RETURN( Mod2Gcc(expr) )
   ELSE
      RETURN( DeclareKnownConstant(location, Mod2Gcc(GetType(des)), Mod2Gcc(expr)) )
   END
END CheckConstant ;


(*
   CodeMakeAdr - code the function MAKEADR.
*)

PROCEDURE CodeMakeAdr (q: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   r       : CARDINAL ;
   n       : CARDINAL ;
   type    : CARDINAL ;
   op      : QuadOperator ;
   bits,
   max,
   tmp,
   res,
   val     : tree ;
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   n := q ;
   REPEAT
      IF op1 > 0
      THEN
         DeclareConstant (CurrentQuadToken, op3)
      END ;
      n := GetNextQuad (n) ;
      GetQuad (n, op, r, op2, op3)
   UNTIL op = FunctValueOp ;

   n := q ;
   GetQuad (n, op, op1, op2, op3) ;
   res := Mod2Gcc (r) ;
   max := GetSizeOfInBits (Mod2Gcc(Address)) ;
   bits := GetIntegerZero (location) ;
   val := GetPointerZero (location) ;
   REPEAT
      location := TokenToLocation (CurrentQuadToken) ;
      IF (op = ParamOp) AND (op1 > 0)
      THEN
         IF GetType (op3) = NulSym
         THEN
            WriteFormat0 ('must supply typed constants to MAKEADR')
         ELSE
            type := GetType (op3) ;
            tmp := BuildConvert (location, GetPointerType (), Mod2Gcc (op3), FALSE) ;
            IF CompareTrees (bits, GetIntegerZero (location)) > 0
            THEN
               tmp := BuildLSL (location, tmp, bits, FALSE)
            END ;
            bits := BuildAdd (location, bits, GetSizeOfInBits (Mod2Gcc (type)), FALSE) ;
            val := BuildLogicalOrAddress (location, val, tmp, FALSE)
         END
      END ;
      SubQuad (n) ;
      n := GetNextQuad (n) ;
      GetQuad (n, op, op1, op2, op3)
   UNTIL op=FunctValueOp ;
   IF CompareTrees(bits, max) > 0
   THEN
      MetaErrorT0 (CurrentQuadToken,
                   'total number of bits specified as parameters to {%kMAKEADR} exceeds address width')
   END ;
   SubQuad(n) ;
   BuildAssignmentStatement (location, res, val)
END CodeMakeAdr ;


(*
   CodeBuiltinFunction - attempts to inline a function. Currently it only
                         inlines the SYSTEM function MAKEADR.
*)

PROCEDURE CodeBuiltinFunction (q: CARDINAL; nth, func, parameter: CARDINAL) ;
BEGIN
   IF nth = 0
   THEN
      InitBuiltinSyms (BuiltinTokenNo) ;
      IF func = MakeAdr
      THEN
         CodeMakeAdr (q, nth, func, parameter)
      END
   END
END CodeBuiltinFunction ;


(*
   FoldMakeAdr - attempts to fold the function MAKEADR.
*)

PROCEDURE FoldMakeAdr (tokenno: CARDINAL; p: WalkAction;
                       q: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   resolved: BOOLEAN ;
   r       : CARDINAL ;
   n       : CARDINAL ;
   op      : QuadOperator ;
   type    : CARDINAL ;
   bits,
   max,
   tmp,
   val     : tree ;
   location: location_t ;
BEGIN
   location := TokenToLocation (tokenno) ;
   resolved := TRUE ;
   n := q ;
   r := op1 ;
   REPEAT
      IF r>0
      THEN
         TryDeclareConstant (tokenno, op3) ;
         IF NOT GccKnowsAbout (op3)
         THEN
            resolved := FALSE
         END
      END ;
      n := GetNextQuad (n) ;
      GetQuad (n, op, r, op2, op3)
   UNTIL op = FunctValueOp ;

   IF resolved AND IsConst (r)
   THEN
      n := q ;
      GetQuad (n, op, op1, op2, op3) ;
      max := GetSizeOfInBits (Mod2Gcc(Address)) ;
      bits := GetIntegerZero (location) ;
      val := GetPointerZero (location) ;
      REPEAT
         location := TokenToLocation (tokenno) ;
         IF (op = ParamOp) AND (op1 > 0)
         THEN
            IF GetType (op3) = NulSym
            THEN
               MetaErrorT0 (tokenno,
                            'constants passed to {%kMAKEADR} must be typed')
            ELSE
               type := GetType (op3) ;
               tmp := BuildConvert (location, GetPointerType (), Mod2Gcc (op3), FALSE) ;
               IF CompareTrees (bits, GetIntegerZero (location)) > 0
               THEN
                  tmp := BuildLSL (location, tmp, bits, FALSE)
               END ;
	       bits := BuildAdd (location, bits, GetSizeOfInBits (Mod2Gcc (type)), FALSE) ;
               val := BuildLogicalOrAddress (location, val, tmp, FALSE)
            END
         END ;
         SubQuad (n) ;
         n := GetNextQuad (n) ;
         GetQuad (n, op, op1, op2, op3)
      UNTIL op = FunctValueOp ;
      IF CompareTrees (bits, max) > 0
      THEN
         MetaErrorT0 (tokenno,
                      'total number of bits specified as parameters to {%kMAKEADR} exceeds address width')
      END ;
      PutConst (r, Address) ;
      AddModGcc (r, DeclareKnownConstant (location, Mod2Gcc (Address), val)) ;
      p (r) ;
      NoChange := FALSE ;
      SubQuad (n)
   END
END FoldMakeAdr ;


(*
   doParam - builds the parameter, op3, which is to be passed to
             procedure, op2.  The number of the parameter is op1.
*)

PROCEDURE doParam (quad: CARDINAL; paramtok: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (paramtok) ;
   DeclareConstant (paramtok, op3) ;
   DeclareConstructor (paramtok, quad, op3) ;
   BuildParam (location, CheckConvertCoerceParameter (paramtok, op1, op2, op3))
END doParam ;


(*
   FoldBuiltin - attempts to fold the gcc builtin function.
*)

PROCEDURE FoldBuiltin (tokenno: CARDINAL; p: WalkAction; q: CARDINAL) ;
VAR
   resolved  : BOOLEAN ;
   procedure,
   r         : CARDINAL ;
   n         : CARDINAL ;
   op1, op2,
   op3       : CARDINAL ;
   op        : QuadOperator ;
   val, call : tree ;
   location  : location_t ;
BEGIN
   GetQuad (q, op, op1, op2, op3) ;
   resolved := TRUE ;
   procedure := NulSym ;
   n := q ;
   r := op1 ;
   REPEAT
      IF r>0
      THEN
         TryDeclareConstant(tokenno, op3) ;
         IF NOT GccKnowsAbout(op3)
         THEN
            resolved := FALSE
         END
      END ;
      IF (op=CallOp) AND (NOT IsProcedure(op3))
      THEN
         (* Cannot fold an indirect procedure function call.  *)
         resolved := FALSE
      END ;
      n := GetNextQuad(n) ;
      GetQuad(n, op, r, op2, op3)
   UNTIL op=FunctValueOp ;

   IF resolved AND IsConst(r)
   THEN
      n := q ;
      GetQuad(n, op, op1, op2, op3) ;
      REPEAT
         IF (op=ParamOp) AND (op1>0)
         THEN
            doParam (tokenno, n, op1, op2, op3)
         ELSIF op=CallOp
         THEN
            procedure := op3
         END ;
         SubQuad(n) ;
         n := GetNextQuad(n) ;
         GetQuad(n, op, op1, op2, op3)
      UNTIL op=FunctValueOp ;

      IF IsProcedureBuiltinAvailable (procedure)
      THEN
         location := TokenToLocation(tokenno) ;
         call := UseBuiltin (tokenno, procedure) ;
         val := BuildFunctValue (location, call) ;
         val := FoldAndStrip (val) ;
         PutConst(r, GetType(procedure)) ;
         AddModGcc(r, DeclareKnownConstant(location, Mod2Gcc(GetType(procedure)), val)) ;
         p(r) ;
         SetLastFunction(NIL)
      ELSE
         MetaErrorT1 (tokenno, 'gcc builtin procedure {%1Ead} cannot be used in a constant expression', procedure) ;
      END ;
      NoChange := FALSE ;
      SubQuad(n)
   END
END FoldBuiltin ;


(*
   FoldBuiltinFunction - attempts to inline a function. Currently it only
                         inlines the SYSTEM function MAKEADR.
*)

PROCEDURE FoldBuiltinFunction (tokenno: CARDINAL; p: WalkAction;
                               q: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF op1=0
   THEN
      (* Must be a function as op1 is the return parameter.  *)
      IF op3=MakeAdr
      THEN
         FoldMakeAdr (tokenno, p, q, op1, op2, op3)
      ELSIF IsProcedure (op3) AND IsProcedureBuiltinAvailable (op3)
      THEN
         FoldBuiltin (tokenno, p, q)
      END
   END
END FoldBuiltinFunction ;


(*
   CodeParam - builds a parameter list.
               Note that we can ignore ModeOfAddr as any lvalue will
               have been created in a preceeding quadruple.
*)

PROCEDURE CodeParam (quad: CARDINAL) ;
VAR
   nopos,
   procedure,
   parameter,
   parampos  : CARDINAL ;
   nth       : CARDINAL ;
   compatible,
   constExpr,
   overflow  : BOOLEAN ;
   op        : QuadOperator ;
BEGIN
   GetQuadOtok (quad, parampos, op,
                nth, procedure, parameter,
                overflow, constExpr,
                nopos, nopos, nopos) ;
   compatible := TRUE ;
   IF nth=0
   THEN
      CodeBuiltinFunction (quad, nth, procedure, parameter)
   ELSE
      IF StrictTypeChecking
      THEN
         IF (nth <= NoOfParamAny (procedure))
         THEN
            compatible := ParameterTypeCompatible (parampos,
                                                   'parameter incompatibility when attempting to pass actual parameter {%2ad} to a {%kVAR} formal parameter {%3Ead} during call to procedure {%1ad}',
                                                   procedure, GetNthParamAny (procedure, nth),
                                                   parameter, nth, IsVarParamAny (procedure, nth))
         END
      END ;

      IF (nth <= NoOfParamAny (procedure)) AND
         IsVarParamAny (procedure, nth) AND IsConst (parameter)
      THEN
         MetaErrorT1 (parampos,
                      'cannot pass a constant {%1Ead} as a VAR parameter', parameter)
      ELSIF IsAModula2Type (parameter)
      THEN
         MetaErrorT2 (parampos,
                      'cannot pass a type {%1Ead} as a parameter to procedure {%2ad}',
                      parameter, procedure)
      ELSIF compatible
      THEN
         doParam (quad, parampos, nth, procedure, parameter)
      END
   END
END CodeParam ;


(*
   Replace - replace the entry for sym in the double entry bookkeeping with sym/tree.
*)

PROCEDURE Replace (sym: CARDINAL; gcc: tree) ;
BEGIN
   IF GccKnowsAbout (sym)
   THEN
      RemoveMod2Gcc (sym)
   END ;
   AddModGcc (sym, gcc)
END Replace ;


(*
   CodeFunctValue - retrieves the function return value and assigns it
                    into a variable.
*)

PROCEDURE CodeFunctValue (location: location_t; op1: CARDINAL) ;
VAR
   call,
   value: tree ;
BEGIN
   (*
      operator : FunctValueOp
      op1 : The Returned Variable
      op3 : The Function Returning this Variable
   *)
   IF EnableSSA AND IsVariableSSA (op1)
   THEN
      call := GetLastFunction () ;
      SetLastFunction (NIL) ;
      Replace (op1, call)
   ELSE
      value := BuildFunctValue (location, Mod2Gcc (op1)) ;
      (* AddStatement (location, CheckCleanup (location, op3, value, call))  *)
      AddStatement (location, value)
   END
END CodeFunctValue ;


(*
   FoldStringLength -
*)

PROCEDURE FoldStringLength (quad: CARDINAL; p: WalkAction) ;
VAR
   op              : QuadOperator ;
   des, none, expr : CARDINAL ;
   stroppos,
   despos, nonepos,
   exprpos         : CARDINAL ;
   constExpr,
   overflowChecking: BOOLEAN ;
   location        : location_t ;
BEGIN
   GetQuadOtok (quad, stroppos, op, des, none, expr,
                overflowChecking, constExpr,
                despos, nonepos, exprpos) ;
   IF IsConstStr (expr) AND IsConstStrKnown (expr)
   THEN
      location := TokenToLocation (stroppos) ;
      PushCard (GetStringLength (exprpos, expr)) ;
      AddModGcc (des, BuildConvert (location, Mod2Gcc (GetType (des)), PopIntegerTree (), FALSE)) ;
      RemoveQuad (p, des, quad)
   END
END FoldStringLength ;


(*
   FoldStringConvertM2nul - attempt to assign the des with the string contents from expr.
                            It also marks the des as a m2 string which must be nul terminated.
                            The front end uses double book keeping and it is easier to have
                            different m2 string symbols each of which map onto a slightly different
                            gcc string tree.
*)

PROCEDURE FoldStringConvertM2nul (quad: CARDINAL; p: WalkAction) ;
VAR
   op              : QuadOperator ;
   des, none, expr : CARDINAL ;
   stroppos,
   despos, nonepos,
   exprpos         : CARDINAL ;
   s               : String ;
   constExpr,
   overflowChecking: BOOLEAN ;
BEGIN
   GetQuadOtok (quad, stroppos, op, des, none, expr,
                overflowChecking, constExpr,
                despos, nonepos, exprpos) ;
   IF IsConstStr (expr) AND IsConstStrKnown (expr)
   THEN
      s := GetStr (exprpos, expr) ;
      PutConstStringKnown (stroppos, des, makekey (string (s)), FALSE, TRUE) ;
      TryDeclareConstant (despos, des) ;
      p (des) ;
      NoChange := FALSE ;
      SubQuad (quad) ;
      s := KillString (s)
   END
END FoldStringConvertM2nul ;


(*
   FoldStringConvertCnul -attempt to assign the des with the string contents from expr.
                          It also marks the des as a C string which must be nul terminated.
*)

PROCEDURE FoldStringConvertCnul (quad: CARDINAL; p: WalkAction) ;
VAR
   op              : QuadOperator ;
   des, none, expr : CARDINAL ;
   stroppos,
   despos, nonepos,
   exprpos         : CARDINAL ;
   s               : String ;
   constExpr,
   overflowChecking: BOOLEAN ;
BEGIN
   GetQuadOtok (quad, stroppos, op, des, none, expr,
                overflowChecking, constExpr,
                despos, nonepos, exprpos) ;
   IF IsConstStr (expr) AND IsConstStrKnown (expr)
   THEN
      s := GetStr (exprpos, expr) ;
      PutConstStringKnown (stroppos, des, makekey (string (s)), TRUE, TRUE) ;
      TryDeclareConstant (despos, des) ;
      p (des) ;
      NoChange := FALSE ;
      SubQuad (quad) ;
      s := KillString (s)
   END
END FoldStringConvertCnul ;


(*
   Addr Operator - generates the address of a variable (op1 = &op3).
*)

PROCEDURE CodeAddr (tokenno: CARDINAL; quad: CARDINAL; op1, op3: CARDINAL) ;
VAR
   value   : tree ;
   type    : CARDINAL ;
   location: location_t ;
BEGIN
   IF IsConst(op3) AND (NOT IsConstString(op3))
   THEN
      MetaErrorT1 (tokenno, 'error in expression, trying to find the address of a constant {%1Ead}', op3)
   ELSE
      IF IsConstString (op3) AND (NOT IsConstStringKnown (op3))
      THEN
         printf1 ("failure in quad: %d\n", quad)
      END ;
      location := TokenToLocation (tokenno) ;
      type := SkipType (GetType (op3)) ;
      DeclareConstant (tokenno, op3) ;  (* We might be asked to find the address of a constant string.  *)
      DeclareConstructor (tokenno, quad, op3) ;
      IF (IsConst (op3) AND (type=Char)) OR IsConstString (op3)
      THEN
         value := BuildStringConstant (KeyToCharStar (GetString (op3)), GetStringLength (tokenno, op3))
      ELSE
         value := Mod2Gcc (op3)
      END ;
      BuildAssignmentStatement (location,
                                Mod2Gcc (op1),
                                BuildAddr (location, value, FALSE))
   END
END CodeAddr ;


PROCEDURE stop ; BEGIN END stop ;

PROCEDURE CheckStop (q: CARDINAL) ;
BEGIN
   IF q=3827
   THEN
      stop
   END
END CheckStop ;


(*
------------------------------------------------------------------------------
   := Operator
------------------------------------------------------------------------------
   Sym1<I> := Sym3<I>           := produces a constant
*)

PROCEDURE FoldBecomes (p: WalkAction; bb: BasicBlock; quad: CARDINAL) ;
VAR
   op            : QuadOperator ;
   des, op2, expr: CARDINAL ;
BEGIN
   IF DeclaredOperandsBecomes (p, quad)
   THEN
      IF (NOT IsConditionalBooleanQuad (quad)) OR IsBasicBlockFirst (bb)
      THEN
         IF TypeCheckBecomes (p, quad)
         THEN
            PerformFoldBecomes (p, quad)
         ELSE
            GetQuad (quad, op, des, op2, expr) ;
            RemoveQuad (p, des, quad)
         END
      END
   END
END FoldBecomes ;


(*
   TryDeclareConst -
*)

PROCEDURE TryDeclareConst (tokenno: CARDINAL; sym: CARDINAL) ;
BEGIN
   (* Check whether expr is a constant literal and if so declare it.  *)
   TryDeclareConstant (tokenno, sym) ;
   (* Check whether expr is a const constructor and if so declare it.  *)
   TryDeclareConstructor (tokenno, sym)
END TryDeclareConst ;


(*
   RemoveQuad - remove quad and ensure p (des) is called.
*)

PROCEDURE RemoveQuad (p: WalkAction; des: CARDINAL; quad: CARDINAL) ;
BEGIN
   p (des) ;
   NoChange := FALSE ;
   SubQuad (quad)
END RemoveQuad ;


(*
   DeclaredOperandsBecomes -
*)

PROCEDURE DeclaredOperandsBecomes (p: WalkAction; quad: CARDINAL) : BOOLEAN ;
VAR
   des, op2, expr     : CARDINAL ;
   constExpr,
   overflowChecking   : BOOLEAN ;
   despos, op2pos,
   exprpos, becomespos: CARDINAL ;
   op                 : QuadOperator ;
BEGIN
   GetQuadOtok (quad, becomespos, op,
                des, op2, expr,
                overflowChecking, constExpr,
                despos, op2pos, exprpos) ;
   Assert (op2pos = UnknownTokenNo) ;
   TryDeclareConst (exprpos, expr) ;
   IF IsConst (des) AND IsConstant (expr)
   THEN
      (* Constant folding taking place, but have we resolved op3 yet?  *)
      IF GccKnowsAbout (expr)
      THEN
         (* Now we can tell gcc about the relationship between des and expr.  *)
         (* RemoveSSAPlaceholder (quad, des) ;  *)
         IF GccKnowsAbout (des)
         THEN
            MetaErrorT1 (despos, 'constant {%1Ead} should not be reassigned', des) ;
            RemoveQuad (p, des, quad) ;
            RETURN FALSE
         ELSE
            RETURN TRUE
         END
      END
   END ;
   RETURN FALSE
END DeclaredOperandsBecomes ;


(*
   TypeCheckBecomes - returns TRUE if the type check succeeds.
*)

PROCEDURE TypeCheckBecomes (p: WalkAction; quad: CARDINAL) : BOOLEAN ;
VAR
   des, op2, expr     : CARDINAL ;
   constExpr,
   overflowChecking   : BOOLEAN ;
   despos, op2pos,
   exprpos, becomespos: CARDINAL ;
   op                 : QuadOperator ;
BEGIN
   GetQuadOtok (quad, becomespos, op,
                des, op2, expr,
                overflowChecking, constExpr,
                despos, op2pos, exprpos) ;
   Assert (op2pos = UnknownTokenNo) ;
   IF StrictTypeChecking AND
      (NOT AssignmentTypeCompatible (despos, "", des, expr))
   THEN
      MetaErrorT2 (MakeVirtualTok (becomespos, despos, exprpos),
                   'assignment check caught mismatch between {%1Ead} and {%2ad}',
                   des, expr) ;
      RemoveQuad (p, des, quad) ;
      RETURN FALSE
   END ;
   RETURN TRUE
END TypeCheckBecomes ;


(*
   PerformFoldBecomes - attempts to fold quad.  It propagates constant strings
                        and attempts to declare des providing it is a constant
                        and expr is resolved.
*)

PROCEDURE PerformFoldBecomes (p: WalkAction; quad: CARDINAL) ;
VAR
   des, op2, expr     : CARDINAL ;
   constExpr,
   overflowChecking   : BOOLEAN ;
   despos, op2pos,
   exprpos, becomespos,
   virtpos            : CARDINAL ;
   op                 : QuadOperator ;
BEGIN
   GetQuadOtok (quad, becomespos, op,
                des, op2, expr,
                overflowChecking, constExpr,
                despos, op2pos, exprpos) ;
   Assert (op2pos = UnknownTokenNo) ;
   IF IsConst (des) AND IsConstString (expr)
   THEN
      IF IsConstStringKnown (expr) AND (NOT IsConstStringKnown (des))
      THEN
         CopyConstString (exprpos, des, expr)
      END
   ELSIF GetType (des) = NulSym
   THEN
      Assert (GetType (expr) # NulSym) ;
      PutConst (des, GetType (expr))
   END ;
   IF GetType (expr) = NulSym
   THEN
      CheckOrResetOverflow (exprpos, Mod2Gcc (expr), MustCheckOverflow (quad)) ;
      AddModGcc (des, Mod2Gcc (expr))
   ELSE
      IF NOT GccKnowsAbout (GetType (des))
      THEN
         RETURN
      END ;
      IF IsProcedure (expr)
      THEN
         AddModGcc (des,
                    BuildConvert (TokenToLocation (exprpos),
                                  Mod2Gcc (GetType (des)),
                                  BuildAddr (TokenToLocation (exprpos),
                                             Mod2Gcc (expr), FALSE), TRUE))
      ELSIF IsValueSolved (expr)
      THEN
         PushValue (expr) ;
         IF IsValueTypeReal ()
         THEN
            CheckOrResetOverflow (exprpos, PopRealTree (), MustCheckOverflow (quad)) ;
            PushValue (expr) ;
            AddModGcc (des, PopRealTree ())
         ELSIF IsValueTypeSet ()
         THEN
            PopValue (des) ;
            PutConstSet (des)
         ELSIF IsValueTypeConstructor () OR IsValueTypeArray () OR IsValueTypeRecord ()
         THEN
            PopValue (des) ;
            PutConstructor (des)
         ELSIF IsValueTypeComplex ()
         THEN
            CheckOrResetOverflow (exprpos, PopComplexTree (), MustCheckOverflow (quad)) ;
            PushValue (expr) ;
            PopValue (des)
         ELSE
            CheckOrResetOverflow (exprpos, PopIntegerTree (), MustCheckOverflow (quad)) ;
            IF GetType (des) = NulSym
            THEN
               PushValue (expr) ;
               AddModGcc (des, PopIntegerTree ())
            ELSE
               virtpos := MakeVirtualTok (becomespos, despos, exprpos) ;
               PushValue (expr) ;
               AddModGcc (des, BuildConvert (TokenToLocation (virtpos),
                                             Mod2Gcc (GetType (des)), PopIntegerTree (), FALSE))
            END
         END
      ELSE
         virtpos := MakeVirtualTok (becomespos, despos, exprpos) ;
         CheckOrResetOverflow (exprpos, Mod2Gcc (des), MustCheckOverflow (quad)) ;
         AddModGcc (des,
                    BuildConvert (TokenToLocation (virtpos),
                                  Mod2Gcc (GetType (des)),
                                  DeclareKnownConstant (TokenToLocation (virtpos),
                                                        Mod2Gcc (GetType (expr)),
                                                        Mod2Gcc (expr)), FALSE))
      END
   END ;
   RemoveQuad (p, des, quad) ;
   Assert (RememberConstant(Mod2Gcc (des)) = Mod2Gcc (des))
END PerformFoldBecomes ;


VAR
   tryBlock: tree ;    (* This must be placed into gccgm2 and it must follow the
                          current function scope - ie it needs work with nested procedures.  *)
   handlerBlock: tree ;


(*
   CodeTry - starts building a GCC 'try' node.
*)

PROCEDURE CodeTry ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   handlerBlock := NIL ;
   tryBlock := BuildTryBegin (location)
END CodeTry ;


(*
   CodeThrow - builds a GCC 'throw' node.
*)

PROCEDURE CodeThrow (value: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   IF value = NulSym
   THEN
      AddStatement (location, BuildThrow (location, tree (NIL)))
   ELSE
      DeclareConstant (CurrentQuadToken, value) ;  (* Checks to see whether it is a constant and declares it.  *)
      AddStatement (location, BuildThrow (location, BuildConvert (location,
                                                                  GetIntegerType (),
                                                                  Mod2Gcc (value), FALSE)))
   END
END CodeThrow ;


PROCEDURE CodeRetry (destQuad: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   BuildGoto (location, string (CreateLabelName (destQuad)))
END CodeRetry ;


PROCEDURE CodeCatchBegin ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   BuildTryEnd (tryBlock) ;
   handlerBlock := BuildCatchBegin (location)
END CodeCatchBegin ;


PROCEDURE CodeCatchEnd ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   tryBlock := BuildCatchEnd (location, handlerBlock, tryBlock) ;
   AddStatement (location, tryBlock)
END CodeCatchEnd ;


(*
   DescribeTypeError -
*)

PROCEDURE DescribeTypeError (token: CARDINAL;
                             op1, op2: CARDINAL) ;
BEGIN
   MetaErrorT2(token, 'incompatible set types in assignment, assignment between {%1ERad} and {%2ad}', op1, op2) ;
   MetaError2('set types are {%1CDtsad} and {%2Dtsad}', op1, op2)
END DescribeTypeError ;


(*
   DefaultConvertGM2 - provides a simple mapping between
                       front end data types and GCC equivalents.
                       This is only used to aid assignment of
                       typed constants.
*)

PROCEDURE DefaultConvertGM2 (sym: CARDINAL) : tree ;
BEGIN
   sym := SkipType (sym) ;
   IF sym=Bitset
   THEN
      RETURN( GetWordType() )
   ELSE
      RETURN( Mod2Gcc(sym) )
   END
END DefaultConvertGM2 ;


(*
   FoldConstBecomes - returns a Tree containing op3.
                      The tree will have been folded and
                      type converted if necessary.
*)

PROCEDURE FoldConstBecomes (tokenno: CARDINAL;
                            op1, op3: CARDINAL) : tree ;
VAR
   t, type : tree ;
   location: location_t ;
BEGIN
   IF IsConstSet(op3) OR ((SkipType(GetType(op3))#NulSym) AND
                          IsSet(SkipType(GetType(op3))))
   THEN
      IF SkipType(GetTypeMode(op1))#SkipType(GetTypeMode(op3))
      THEN
         DescribeTypeError (tokenno, op1, op3) ;
         (* Assigning an errant op3 might ICE, therefore it is safer to return op1.  *)         
         RETURN( Mod2Gcc (op1) )
      END
   END ;
   location := TokenToLocation (tokenno) ;
   TryDeclareConstant (tokenno, op3) ;
   t := Mod2Gcc (op3) ;
   Assert (t#NIL) ;
   IF IsConstant (op3)
   THEN
      IF IsProcedure (op3)
      THEN
         RETURN t
      ELSIF (NOT IsConstString (op3)) AND (NOT IsConstSet (op3)) AND
         (SkipType (GetType (op3)) # SkipType (GetType (op1)))
      THEN
         type := DefaultConvertGM2 (GetType(op1)) ;  (* do we need this now? --fixme-- *)
         t := ConvertConstantAndCheck (location, type, t)
      ELSIF GetType (op1) # NulSym
      THEN
         t := StringToChar (Mod2Gcc (op3), GetType (op1), op3)
      END
   END ;
   RETURN( t )
END FoldConstBecomes ;


(*
   PrepareCopyString - returns two trees:
                       length    number of bytes to be copied (including the nul if room)
                       srcTreeType the new string type (with the extra nul character).

                       Pre condition:  destStrType the dest type string.
                                       src is the original string (without a nul)
                                       to be copied.
                       Post condition: TRUE or FALSE is returned.
                                       if true length and srcTreeType will be assigned
                                       else length is set to the maximum length to be
                                            copied and srcTree is set to the max length
                                            which fits in dest.
*)

PROCEDURE PrepareCopyString (tokenno: CARDINAL; VAR length, srcTree: tree;
                             src, destStrType: CARDINAL) : BOOLEAN ;
VAR
   location : location_t ;
   intLength: INTEGER ;
BEGIN
   location := TokenToLocation (tokenno) ;
   Assert (IsArray (SkipType (destStrType))) ;
   (* Handle string assignments:
      VAR
         str: ARRAY [0..10] OF CHAR ;
         ch : CHAR ;

         str := 'abcde' but not ch := 'a'
   *)
   IF GetType (src) = Char
   THEN
      (*
       *  Create string from char and add nul to the end, nul is
       *  added by BuildStringConstant.  In modula-2 an array must
       *  have at least one element.
       *)
      length := GetIntegerOne (location) ;
      PushIntegerTree (FindSize (tokenno, src)) ;
      PushIntegerTree (FindSize (tokenno, destStrType)) ;
      IF Less (tokenno)
      THEN
         (* There is room for the extra <nul> character.  *)
         length := BuildAdd (location, length,
                             GetIntegerOne (location), FALSE)
      END
   ELSE
      PushIntegerTree (FindSize (tokenno, src)) ;
      PushIntegerTree (FindSize (tokenno, destStrType)) ;
      IF Less (tokenno)
      THEN
         (* There is room for the extra <nul> character.  *)
         length := BuildAdd (location, FindSize (tokenno, src),
                             GetIntegerOne (location), FALSE) ;
         srcTree := Mod2Gcc (src)
      ELSE
         (* We need to truncate the <nul> at least.  *)
         length := FindSize (tokenno, destStrType) ;
         PushIntegerTree (FindSize (tokenno, src)) ;
         PushIntegerTree (length) ;
         (* Greater or Equal so return max characters in the array.  *)
         IF Gre (tokenno)
         THEN
            (* Create a new string without non nul characters to be gimple safe.
               But return FALSE indicating an overflow.  *)
            intLength := GetCstInteger (length) ;
            srcTree := BuildStringConstant (KeyToCharStar (GetString (src)), intLength) ;
            srcTree := ConvertString (Mod2Gcc (destStrType), srcTree) ;
            RETURN FALSE
         END
      END
   END ;
   intLength := GetCstInteger (length) ;
   srcTree := BuildStringConstant (KeyToCharStar (GetString (src)), intLength) ;
   srcTree := ConvertString (Mod2Gcc (destStrType), srcTree) ;
   RETURN TRUE
END PrepareCopyString ;


(*
   checkArrayElements - return TRUE if des or expr are not arrays.
                        If they are arrays and have different number of
                        elements return FALSE, otherwise TRUE.
*)

PROCEDURE checkArrayElements (des, expr: CARDINAL; virtpos, despos, exprpos: CARDINAL) : BOOLEAN ;
VAR
   e1, e3: tree ;
   t1, t3: CARDINAL ;
BEGIN
   t1 := GetType (des) ;
   t3 := GetType (expr) ;
   IF (t1 # NulSym) AND (t3 # NulSym) AND
      IsArray (SkipType (GetType (expr))) AND IsArray (SkipType (GetType (des)))
   THEN
      (* both arrays continue checking *)
      e1 := GetArrayNoOfElements (TokenToLocation (despos),
                                  Mod2Gcc (SkipType (GetType (des)))) ;
      e3 := GetArrayNoOfElements (TokenToLocation (exprpos),
                                  Mod2Gcc (SkipType (GetType (expr)))) ;
      IF CompareTrees (e1, e3) # 0
      THEN
         MetaErrorT2 (virtpos,
                      'not allowed to assign array {%2Ead} to {%1ad} as they have a different number of elements',
                      des, expr) ;
         RETURN( FALSE )
      END
   END ;
   RETURN( TRUE )
END checkArrayElements ;


(*
   CodeInitAddress -
*)

PROCEDURE CodeInitAddress (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   DeclareConstant (CurrentQuadToken, op3) ;  (* checks to see whether it is a constant and declares it *)
   DeclareConstructor (CurrentQuadToken, quad, op3) ;

   location := TokenToLocation (CurrentQuadToken) ;

   Assert (op2 = NulSym) ;
   Assert (GetMode (op1) = LeftValue) ;
   BuildAssignmentStatement (location,
                             Mod2Gcc (op1),
                             BuildConvert (location, GetPointerType (), Mod2Gcc (op3), FALSE))
END CodeInitAddress ;


(*
   checkRecordTypes - returns TRUE if des is not a record or if the record
                      is the same type as expr.
*)

PROCEDURE checkRecordTypes (des, expr: CARDINAL; virtpos: CARDINAL) : BOOLEAN ;
VAR
   t1, t2: CARDINAL ;
BEGIN
   IF (GetType (des) = NulSym) OR (GetMode (des) = LeftValue)
   THEN
      RETURN( TRUE )
   ELSE
      t1 := SkipType (GetType (des)) ;
      IF IsRecord (t1)
      THEN
         IF GetType (expr) = NulSym
         THEN
            MetaErrorT2 (virtpos,
                         'cannot assign an operand of type {%1Ets} to a record type {%2tsa}',
                         expr, des) ;
            RETURN( FALSE )
         ELSE
            t2 := SkipType (GetType (expr)) ;
	    IF t1 = t2
            THEN
               RETURN( TRUE )
            ELSE
               MetaErrorT2 (virtpos,
                            'cannot assign an operand of type {%1ts} to a record type {%2tsa}',
                            expr, des) ;
	       RETURN( FALSE )
            END
         END
      END
   END ;
   RETURN( TRUE )
END checkRecordTypes ;


(*
   checkIncorrectMeta - checks to see if des and expr are assignment compatible is allows
                        generic system types to be assigned.
*)

PROCEDURE checkIncorrectMeta (des, expr: CARDINAL; virtpos: CARDINAL) : BOOLEAN ;
VAR
   t1, t2: CARDINAL ;
BEGIN
   t1 := SkipType (GetType (des)) ;
   t2 := SkipType (GetType (expr)) ;
   IF (t1 = NulSym) OR (GetMode(des) = LeftValue) OR
      (t2 = NulSym) OR (GetMode(expr) = LeftValue)
   THEN
      RETURN( TRUE )
   ELSIF (t1 # t2) AND (NOT IsGenericSystemType (t1)) AND (NOT IsGenericSystemType (t2))
   THEN
      IF IsArray (t1) OR IsSet (t1) OR IsRecord (t1)
      THEN
         IF NOT IsAssignmentCompatible (t1, t2)
         THEN
            ErrorMessageDecl (virtpos,
                              'illegal assignment error between {%1Etad} and {%2tad}',
                              des, expr, TRUE) ;
	    RETURN( FALSE )
         END
      END
   END ;
   RETURN( TRUE )
END checkIncorrectMeta ;


(*
   checkBecomes - returns TRUE if the checks pass.
*)

PROCEDURE checkBecomes (des, expr: CARDINAL; virtpos, despos, exprpos: CARDINAL) : BOOLEAN ;
BEGIN
   IF (NOT checkArrayElements (des, expr, virtpos, despos, exprpos)) OR
      (NOT checkRecordTypes (des, expr, virtpos)) OR
      (NOT checkIncorrectMeta (des, expr, virtpos))
   THEN
      RETURN FALSE
   END ;
   RETURN TRUE
END checkBecomes ;


(*
   checkDeclare - checks to see if sym is declared and if it is not then declare it.
*)

PROCEDURE checkDeclare (sym: CARDINAL) ;
BEGIN
   IF IsTemporary (sym) AND IsVariableSSA (sym) AND (NOT GccKnowsAbout (sym))
   THEN
      DeclareLocalVariable (sym)
   END
END checkDeclare ;


(*
   PerformCodeBecomes -
*)

PROCEDURE PerformCodeBecomes (location: location_t;
                              virtpos: CARDINAL; des, expr: CARDINAL) ;
VAR
   destree, exprtree: tree ;
BEGIN
   destree := Mod2Gcc (des) ;
   exprtree := FoldConstBecomes (virtpos, des, expr) ;
   IF IsVar (des) AND IsVariableSSA (des)
   THEN
      Replace (des, exprtree)
   ELSIF IsGccStrictTypeEquivalent (destree, exprtree)
   THEN
      BuildAssignmentStatement (location, destree, exprtree)
   ELSE
      CopyByField (location, destree, exprtree)
   END
END PerformCodeBecomes ;


(*
------------------------------------------------------------------------------
   := Operator
------------------------------------------------------------------------------
   Sym1<I> := Sym3<I>           := produces a constant
   Sym1<O> := Sym3<O>           := has the effect Mem[Sym1<I>] := Mem[Sym3<I>]
*)

PROCEDURE CodeBecomes (quad: CARDINAL) ;
VAR
   constExpr,
   overflowChecking: BOOLEAN ;
   op              : QuadOperator ;
   des, op2, expr   : CARDINAL ;
   virtpos,
   becomespos,
   despos,
   op2pos,
   exprpos          : CARDINAL ;
   length,
   exprt            : tree ;
   location        : location_t ;
BEGIN
   GetQuadOtok (quad, becomespos, op, des, op2, expr,
                overflowChecking, constExpr,
                despos, op2pos, exprpos) ;
   Assert (op2pos = UnknownTokenNo) ;
   DeclareConstant (exprpos, expr) ;  (* Check to see whether expr is a constant and declare it.  *)
   DeclareConstructor (exprpos, quad, expr) ;
   virtpos := MakeVirtualTok (becomespos, despos, exprpos) ;
   location := TokenToLocation (virtpos) ;

   IF StrictTypeChecking AND
      (NOT AssignmentTypeCompatible (virtpos, "", des, expr))
   THEN
      ErrorMessageDecl (virtpos,
                        'assignment check caught mismatch between {%1Ead} and {%2ad}',
                        des, expr, TRUE)
   END ;
   IF IsConstString (expr) AND (NOT IsConstStringKnown (expr))
   THEN
      MetaErrorT2 (virtpos,
                   'internal error: CodeBecomes {%1Aad} in quad {%2n}', des, quad)
   END ;
   IF IsConst (des) AND (NOT GccKnowsAbout (des))
   THEN
      ConstantKnownAndUsed (des, CheckConstant (virtpos, des, expr))
   ELSIF IsConstString (expr) AND (SkipTypeAndSubrange (GetType (des)) # Char)
   THEN
      checkDeclare (des) ;
      IF NOT PrepareCopyString (becomespos, length, exprt, expr, SkipType (GetType (des)))
      THEN
         ErrorMessageDecl (virtpos,
                           'string constant {%1Ea} is too large to be assigned to the array {%2ad}',
                           expr, des, TRUE)
      END ;
      AddStatement (location,
                    MaybeDebugBuiltinMemcpy (location,
                                             BuildAddr (location, Mod2Gcc (des), FALSE),
                                             BuildAddr (location, exprt, FALSE),
                                             length))
   ELSE
      IF ((IsGenericSystemType(SkipType(GetType(des))) #
           IsGenericSystemType(SkipType(GetType(expr)))) OR
          (IsUnbounded(SkipType(GetType(des))) AND
           IsUnbounded(SkipType(GetType(expr))) AND
           (IsGenericSystemType(SkipType(GetType(GetType(des)))) #
            IsGenericSystemType(SkipType(GetType(GetType(expr))))))) AND
         (NOT IsConstant(expr))
      THEN
         checkDeclare (des) ;
         AddStatement (location,
                       MaybeDebugBuiltinMemcpy (location,
                                                BuildAddr(location, Mod2Gcc (des), FALSE),
                                                BuildAddr(location, Mod2Gcc (expr), FALSE),
                                                BuildSize(location, Mod2Gcc (des), FALSE)))
      ELSE
         IF checkBecomes (des, expr, virtpos, despos, exprpos)
         THEN
            PerformCodeBecomes (location, virtpos, des, expr)
         ELSE
            SubQuad (quad)  (* We don't want multiple errors for the quad.  *)
         END
      END
   END
END CodeBecomes ;


(*
   LValueToGenericPtr - returns a Tree representing symbol, sym.
                        It coerces a lvalue into an internal pointer type
*)

PROCEDURE LValueToGenericPtr (location: location_t; sym: CARDINAL) : tree ;
VAR
   t: tree ;
BEGIN
   t := Mod2Gcc (sym) ;
   IF t = NIL
   THEN
      InternalError ('expecting symbol to be resolved')
   END ;
   IF GetMode (sym) = LeftValue
   THEN
      t := BuildConvert (location, GetPointerType (), t, FALSE)
   END ;
   RETURN t
END LValueToGenericPtr ;


(*
   LValueToGenericPtrOrConvert - if sym is an lvalue then convert to pointer type
                                 else convert to type, type. Return the converted tree.
*)

PROCEDURE LValueToGenericPtrOrConvert (sym: CARDINAL; type: tree) : tree ;
VAR
   n       : tree ;
   location: location_t ;
BEGIN
   n := Mod2Gcc (sym) ;
   location := TokenToLocation (GetDeclaredMod (sym)) ;
   IF n = NIL
   THEN
      InternalError ('expecting symbol to be resolved')
   END ;
   IF GetMode (sym) = LeftValue
   THEN
      n := BuildConvert (location, GetPointerType (), n, FALSE)
   ELSE
      n := BuildConvert (location, type, n, FALSE)
   END ;
   RETURN n
END LValueToGenericPtrOrConvert ;


(*
   ZConstToTypedConst - checks whether op1 and op2 are constants and
                        coerces, t, appropriately.
*)

PROCEDURE ZConstToTypedConst (t: tree; op1, op2: CARDINAL) : tree ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(GetDeclaredMod(op2)) ;
   IF IsConst(op1) AND IsConst(op2)
   THEN
      (* leave, Z type, alone *)
      RETURN( t )
   ELSIF IsConst(op1)
   THEN
      IF GetMode(op2)=LeftValue
      THEN
         (* convert, Z type const into type of non constant operand *)
         RETURN( BuildConvert(location, GetPointerType(), t, FALSE) )
      ELSE
         (* convert, Z type const into type of non constant operand *)
         RETURN( BuildConvert(location, Mod2Gcc(FindType(op2)), t, FALSE) )
      END
   ELSIF IsConst(op2)
   THEN
      IF GetMode(op1)=LeftValue
      THEN
         (* convert, Z type const into type of non constant operand *)
         RETURN( BuildConvert(location, GetPointerType(), t, FALSE) )
      ELSE
         (* convert, Z type const into type of non constant operand *)
         RETURN( BuildConvert(location, Mod2Gcc(FindType(op1)), t, FALSE) )
      END
   ELSE
      (* neither operands are constants, leave alone *)
      RETURN( t )
   END
END ZConstToTypedConst ;


(*
   FoldBinary - check whether we can fold the binop operation.
*)

PROCEDURE FoldBinary (tokenno: CARDINAL; p: WalkAction; binop: BuildBinProcedure;
                      quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   tl, tr, tv, resType: tree ;
   location           : location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   TryDeclareConstant(tokenno, op3) ;
   TryDeclareConstant(tokenno, op2) ;
   location := TokenToLocation(tokenno) ;
   IF IsConst(op2) AND IsConst(op3)
   THEN
      IF GccKnowsAbout(op2) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            Assert(MixTypes(FindType(op3), FindType(op2), tokenno)#NulSym) ;
            PutConst(op1, MixTypes(FindType(op3), FindType(op2), tokenno)) ;

            tl := LValueToGenericPtr(location, op2) ;
            tr := LValueToGenericPtr(location, op3) ;

            IF GetType(op1)=NulSym
            THEN
               resType := GetM2ZType()
            ELSE
               resType := Mod2Gcc(GetType(op1))
            END ;

            tl := BuildConvert(location, resType, tl, FALSE) ;
            tr := BuildConvert(location, resType, tr, FALSE) ;

            tv := binop(location, tl, tr, TRUE) ;
            CheckOrResetOverflow(tokenno, tv, MustCheckOverflow(quad)) ;

            AddModGcc(op1, DeclareKnownConstant(location, resType, tv)) ;

            p(op1) ;
            NoChange := FALSE ;
            SubQuad(quad)
         ELSE
            (* we can still fold the expression, but not the assignment,
               however, we will not do this here but in CodeBinary
             *)
         END
      END
   END
END FoldBinary ;


(*
   ConvertBinaryOperands -
*)

PROCEDURE ConvertBinaryOperands (location: location_t; VAR tl, tr: tree; type, op2, op3: CARDINAL) ;
BEGIN
   tl := NIL ;
   tr := NIL ;
   IF GetMode(op2)=LeftValue
   THEN
      tl := LValueToGenericPtr(location, op2) ;
      type := Address
   END ;
   IF GetMode(op3)=LeftValue
   THEN
      tr := LValueToGenericPtr(location, op3) ;
      type := Address
   END ;
   IF (tl=NIL) AND (tr=NIL)
   THEN
      tl := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(op2), FALSE) ;
      tr := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(op3), FALSE)
   ELSIF tl=NIL
   THEN
      tl := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(op2), FALSE)
   ELSIF tr=NIL
   THEN
      tr := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(op3), FALSE)
   END
END ConvertBinaryOperands ;


(*
   CodeBinaryCheck - encode a binary arithmetic operation.
*)

PROCEDURE CodeBinaryCheck (binop: BuildBinCheckProcedure; quad: CARDINAL) ;
VAR
   op        : QuadOperator ;
   op1, op2,
   op3       : CARDINAL ;
   op1pos,
   op2pos,
   op3pos,
   lowestType,
   type      : CARDINAL ;
   min, max,
   lowest,
   tv,
   tl, tr    : tree ;
   location  : location_t ;
BEGIN
   (* firstly ensure that constant literals are declared.  *)
   GetQuadtok (quad, op, op1, op2, op3, op1pos, op2pos, op3pos) ;
   DeclareConstant (op3pos, op3) ;
   DeclareConstant (op2pos, op2) ;
   location := TokenToLocation (op1pos) ;

   type := MixTypesBinary (op2, op3, op1pos, MustCheckOverflow (quad)) ;
   ConvertBinaryOperands (location, tl, tr, type, op2, op3) ;

   lowestType := GetLType (op1) ;
   lowest := Mod2Gcc (lowestType) ;
   IF GetMinMax (CurrentQuadToken, lowestType, min, max)
   THEN
      tv := binop (location, tl, tr, lowest, min, max)
   ELSE
      tv := binop (location, tl, tr, NIL, NIL, NIL)
   END ;
   CheckOrResetOverflow (op1pos, tv, MustCheckOverflow (quad)) ;
   IF IsConst (op1)
   THEN
      (* still have a constant which was not resolved, pass it to gcc.  *)
      Assert (MixTypes (FindType (op3), FindType (op2), op3pos) # NulSym) ;

      PutConst (op1, MixTypes (FindType (op3), FindType (op2), op3pos)) ;
      ConstantKnownAndUsed (op1, DeclareKnownConstant (location, Mod2Gcc (GetType (op3)), tv))
   ELSE
      IF EnableSSA AND IsVariableSSA (op1)
      THEN
         Replace (op1, tv)
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (op1), tv)
      END
   END
END CodeBinaryCheck ;


(*
   MixTypesBinary - depending upon overflowCheck do not check pointer arithmetic.
*)

PROCEDURE MixTypesBinary (left, right: CARDINAL;
                          tokpos: CARDINAL; overflowCheck: BOOLEAN) : CARDINAL ;
BEGIN
   IF (NOT overflowCheck) AND
      (IsPointer (GetTypeMode (left)) OR IsPointer (GetTypeMode (right)))
   THEN
      RETURN Address
   ELSE
      RETURN MixTypesDecl (left, right, FindType (left), FindType (right), tokpos)
   END
END MixTypesBinary ;


(*
   CodeBinary - encode a binary arithmetic operation.
*)

PROCEDURE CodeBinary (binop: BuildBinProcedure; quad: CARDINAL) ;
VAR
   op      : QuadOperator ;
   op1, op2,
   op3     : CARDINAL ;
   op1pos,
   op2pos,
   op3pos,
   type    : CARDINAL ;
   tv,
   tl, tr  : tree ;
   location: location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   GetQuadtok (quad, op, op1, op2, op3, op1pos, op2pos, op3pos) ;
   DeclareConstant (op3pos, op3) ;
   DeclareConstant (op2pos, op2) ;
   location := TokenToLocation (op1pos) ;

   type := MixTypesBinary (op2, op3, op1pos, MustCheckOverflow (quad)) ;
   ConvertBinaryOperands (location, tl, tr, type, op2, op3) ;

   tv := binop (location, tl, tr, FALSE) ;
   CheckOrResetOverflow (op1pos, tv, MustCheckOverflow(quad)) ;
   IF IsConst (op1)
   THEN
      (* still have a constant which was not resolved, pass it to gcc *)
      Assert(MixTypes(FindType(op3), FindType(op2), op1pos)#NulSym) ;

      PutConst (op1, MixTypes (FindType (op3), FindType (op2), op1pos)) ;
      ConstantKnownAndUsed (op1, DeclareKnownConstant (location, Mod2Gcc(GetType(op3)), tv))
   ELSE
      IF EnableSSA AND IsVariableSSA (op1)
      THEN
         Replace (op1, tv)
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (op1), tv)
      END
   END
END CodeBinary ;


(*
   NoWalkProcedure -
*)

PROCEDURE NoWalkProcedure (param: CARDINAL <* unused *>) ;
BEGIN
END NoWalkProcedure ;


(*
   CheckBinaryExpressionTypes - returns TRUE if all expression checks pass.
                                If the expression check fails quad is removed,
                                the walk procedure (des) is called and NoChange is
                                set to FALSE.
*)

PROCEDURE CheckBinaryExpressionTypes (quad: CARDINAL; p: WalkAction) : BOOLEAN ;
VAR
   lefttype,
   righttype,
   des, left, right: CARDINAL ;
   typeChecking,
   constExpr,
   overflowChecking: BOOLEAN ;
   despos, leftpos,
   rightpos,
   operatorpos,
   subexprpos      : CARDINAL ;
   op              : QuadOperator ;
BEGIN
   GetQuadOTypetok (quad, operatorpos, op,
                    des, left, right,
                    overflowChecking, typeChecking, constExpr,
                    despos, leftpos, rightpos) ;
   IF typeChecking AND (op # LogicalRotateOp) AND (op # LogicalShiftOp)
   THEN
      subexprpos := MakeVirtualTok (operatorpos, leftpos, rightpos) ;
      lefttype := GetType (left) ;
      righttype := GetType (right) ;
      IF StrictTypeChecking AND
         (NOT ExpressionTypeCompatible (subexprpos, "", lefttype, righttype,
                                        StrictTypeChecking, FALSE))
      THEN
         MetaErrorT2 (subexprpos,
                      'expression mismatch between {%1Etad} and {%2tad}',
                      left, right) ;
         NoChange := FALSE ;
         SubQuad (quad) ;
         p (des) ;
         RETURN FALSE
      END ;
      (* --fixme-- the ExpressionTypeCompatible above should be enough
         and the code below can be removed once ExpressionTypeCompatible
         is bug free.  *)
      IF NOT IsExpressionCompatible (lefttype, righttype)
      THEN
         ErrorMessageDecl (subexprpos,
                           'expression mismatch between {%1Etad} and {%2tad}',
                           left, right, TRUE) ;
         NoChange := FALSE ;
         SubQuad (quad) ;
         p (des) ;
         RETURN FALSE
      END
   END ;
   RETURN TRUE
END CheckBinaryExpressionTypes ;


(*
   CheckElementSetTypes - returns TRUE if all expression checks pass.
                          If the expression check fails quad is removed,
                          the walk procedure (des) is called and NoChange is
                          set to FALSE.
*)

PROCEDURE CheckElementSetTypes (quad: CARDINAL) : BOOLEAN ;
VAR
   lefttype,
   righttype,
   ignore, left, right: CARDINAL ;
   constExpr,
   overflowChecking: BOOLEAN ;
   ignorepos,
   leftpos,
   rightpos,
   operatorpos,
   subexprpos      : CARDINAL ;
   op              : QuadOperator ;
BEGIN
   GetQuadOtok (quad, operatorpos, op,
                left, right, ignore,
                overflowChecking, constExpr,
                leftpos, rightpos, ignorepos) ;
   subexprpos := MakeVirtualTok (operatorpos, leftpos, rightpos) ;
   lefttype := GetType (left) ;
   righttype := GetType (right) ;
   (* --fixme-- the ExpressionTypeCompatible below does not always catch
      type errors, it needs to be fixed and then some of the subsequent tests
      can be removed (and/or this procedure function rewritten).  *)
   IF StrictTypeChecking AND
      (NOT ExpressionTypeCompatible (subexprpos, "", lefttype, righttype,
                                     StrictTypeChecking, TRUE))
   THEN
      MetaErrorT2 (subexprpos,
                   'the types used in expression {%1Etad} {%kIN} {%2tad} are incompatible',
                   left, right) ;
      NoChange := FALSE ;
      SubQuad (quad) ;
      RETURN FALSE
   END ;
   IF (righttype = NulSym) OR (NOT IsSet (SkipType (righttype)))
   THEN
      MetaErrorT1 (rightpos,
                   'an {%kIN} expression is expecting {%1Etad} to be a {%kSET} type',
                   right) ;
      NoChange := FALSE ;
      SubQuad (quad) ;
      RETURN FALSE
   END ;
   righttype := GetType (SkipType (righttype)) ;
   (* Now fall though and compare the set element left against the type of set righttype.  *)
   IF NOT IsExpressionCompatible (lefttype, righttype)
   THEN
      ErrorMessageDecl (subexprpos,
                        'the types used in expression {%1Etad} {%kIN} {%2tad} are incompatible',
                        left, right, TRUE) ;
      NoChange := FALSE ;
      SubQuad (quad) ;
      RETURN FALSE
   END ;
   RETURN TRUE
END CheckElementSetTypes ;


(*
   CodeBinarySet - encode a binary set arithmetic operation.
                   Set operands may be longer than a word.
*)

PROCEDURE CodeBinarySet (binop: BuildBinProcedure; doOp: DoProcedure;
                         quad: CARDINAL) ;
VAR
   location        : location_t ;
   constExpr,
   overflowChecking: BOOLEAN ;
   op              : QuadOperator ;
   virttoken,
   virtexpr,
   des,
   left,
   right,
   despos,
   leftpos,
   rightpos,
   operatorpos     : CARDINAL ;
BEGIN
   GetQuadOtok (quad, operatorpos, op, des, left, right,
                overflowChecking, constExpr,
                despos, leftpos, rightpos) ;

   (* Firstly ensure that constant literals are declared.  *)
   DeclareConstant (rightpos, right) ;
   DeclareConstant (leftpos, left) ;
   DeclareConstructor (rightpos, quad, right) ;
   DeclareConstructor (leftpos, quad, left) ;

   virttoken := MakeVirtualTok (operatorpos, despos, rightpos) ;
   location := TokenToLocation (virttoken) ;
   IF CheckBinaryExpressionTypes (quad, NoWalkProcedure)
   THEN
      IF IsConst (des)
      THEN
         virtexpr := MakeVirtualTok (operatorpos, leftpos, rightpos) ;
         IF IsValueSolved (left) AND IsValueSolved (right)
         THEN
            Assert (MixTypes (FindType (right), FindType (left), virtexpr) # NulSym) ;
            PutConst (des, FindType (right)) ;
            PushValue (left) ;
            PushValue (right) ;
            doOp (virttoken) ;
            PopValue (des) ;
            PutConstSet (des)
         ELSE
            MetaErrorT0 (virtexpr, '{%E}constant expression cannot be evaluated')
         END
      ELSE
         checkDeclare (des) ;
         BuildBinaryForeachWordDo (location,
                                   Mod2Gcc (SkipType (GetType (des))),
                                   Mod2Gcc (des), Mod2Gcc (left), Mod2Gcc (right), binop,
                                   GetMode (des) = LeftValue,
                                   GetMode (left) = LeftValue,
                                   GetMode (right) = LeftValue,
                                   IsConst (des),
                                   IsConst (left),
                                   IsConst (right))
      END
   END
END CodeBinarySet ;


(*
   CheckUnaryOperand - checks to see whether operand is using a generic type.
*)

PROCEDURE CheckUnaryOperand (quad: CARDINAL; operand: CARDINAL) : BOOLEAN ;
VAR
   type  : CARDINAL ;
   s, op : String ;
BEGIN
   type := SkipType (GetType (operand)) ;
   IF (Word=type) OR IsWordN (type) OR (Byte=type) OR (Loc=type)
   THEN
      op := GetM2OperatorDesc (GetQuadOp (quad)) ;
      s := InitString ('operand of type {%1Ets} is not allowed in an unary expression') ;
      IF op # NIL
      THEN
         s := ConCatChar (s, ' ') ;
         s := ConCat (s, Mark (op))
      END ;
      MetaErrorStringT1 (CurrentQuadToken, s, operand) ;
      RETURN FALSE
   END ;
   RETURN TRUE
END CheckUnaryOperand ;


(*
   UnaryOperand - returns TRUE if operand is acceptable for
                  unary operator: + -.  If FALSE
                  is returned, an error message will be generated
                  and the quad is deleted.
*)

PROCEDURE UnaryOperand (quad: CARDINAL; operand: CARDINAL) : BOOLEAN ;
BEGIN
   IF NOT CheckUnaryOperand (quad, operand)
   THEN
      SubQuad (quad) ;  (* We do not want multiple copies of the same error.  *)
      RETURN FALSE
   END ;
   RETURN TRUE
END UnaryOperand ;


(*
   CheckBinaryOperand - checks to see whether operand is using a generic type.
*)

PROCEDURE CheckBinaryOperand (quad: CARDINAL; isleft: BOOLEAN;
                              operand: CARDINAL; result: BOOLEAN) : BOOLEAN ;
VAR
   type  : CARDINAL ;
   qop   : QuadOperator ;
   op1,
   op2,
   op3,
   op1pos,
   op2pos,
   op3pos: CARDINAL ;
   s, op : String ;
BEGIN
   type := SkipType (GetType (operand)) ;
   IF (Word=type) OR IsWordN (type) OR (Byte=type) OR (Loc=type)
   THEN
      GetQuadtok (quad, qop, op1, op2, op3,
                  op1pos, op2pos, op3pos) ;
         op := GetM2OperatorDesc (GetQuadOp (quad)) ;
      IF isleft
      THEN
         s := InitString ('left operand {%1Ea} of type {%1Ets} is not allowed in binary expression')
      ELSE
         s := InitString ('right operand {%1Ea} of type {%1Ets} is not allowed in binary expression')
      END ;
      IF op # NIL
      THEN
         s := ConCatChar (s, ' ') ;
         s := ConCat (s, Mark (op))
      END ;
      MetaErrorStringT1 (op1pos, s, operand) ;
      RETURN FALSE
   END ;
   RETURN result
END CheckBinaryOperand ;


(*
   BinaryOperands - returns TRUE if, l, and, r, are acceptable for
                    binary operator: + - / * and friends.  If FALSE
                    is returned, an error message will be generated
                    and the, quad, is deleted.
*)

PROCEDURE BinaryOperands (quad: CARDINAL; l, r: CARDINAL) : BOOLEAN ;
VAR
   result: BOOLEAN ;
BEGIN
   result := CheckBinaryOperand (quad, TRUE, l, TRUE) ;
   result := CheckBinaryOperand (quad, FALSE, r, result) ;
   IF NOT result
   THEN
      SubQuad (quad)   (* We do not want multiple copies of the same error.  *)
   END ;
   RETURN result
END BinaryOperands ;


(*
   IsConstStr - returns TRUE if sym is a constant string or a char constant.
*)

PROCEDURE IsConstStr (sym: CARDINAL) : BOOLEAN ;
BEGIN
   RETURN IsConstString (sym) OR (IsConst (sym) AND (GetSType (sym) = Char))
END IsConstStr ;


(*
   IsConstStrKnown - returns TRUE if sym is a constant string or a char constant
                     which is known.
*)

PROCEDURE IsConstStrKnown (sym: CARDINAL) : BOOLEAN ;
BEGIN
   RETURN (IsConstString (sym) AND IsConstStringKnown (sym)) OR
          (IsConst (sym) AND (GetSType (sym) = Char))
END IsConstStrKnown ;


(*
   GetStr - return a string containing a constant string value associated with sym.
            A nul char constant will return an empty string.
*)

PROCEDURE GetStr (tokenno: CARDINAL; sym: CARDINAL) : String ;
VAR
   ch: CHAR ;
BEGIN
   Assert (IsConst (sym)) ;
   IF IsConstString (sym)
   THEN
      RETURN InitStringCharStar (KeyToCharStar (GetString (sym)))
   ELSE
      Assert (GetSType (sym) = Char) ;
      PushValue (sym) ;
      ch := PopChar (tokenno) ;
      RETURN InitStringChar (ch)
   END
END GetStr ;


(*
   FoldAdd - check addition for constant folding.  It checks for conststrings
             overloading the +.
*)

PROCEDURE FoldAdd (tokenno: CARDINAL; p: WalkAction;
                   quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   s: String ;
BEGIN
   IF IsConstStr (op2) AND IsConstStr (op3)
   THEN
      IF IsConstStrKnown (op2) AND IsConstStrKnown (op3)
      THEN
         (* Handle special addition for constant strings.  *)
         s := Dup (GetStr (tokenno, op2)) ;
         s := ConCat (s, GetStr (tokenno, op3)) ;
         PutConstStringKnown (tokenno, op1, makekey (string (s)), FALSE, TRUE) ;
         TryDeclareConstant (tokenno, op1) ;
         p (op1) ;
         NoChange := FALSE ;
         SubQuad (quad) ;
         s := KillString (s)
      END
   ELSE
      FoldArithAdd (tokenno, p, quad, op1, op2, op3)
   END
END FoldAdd ;


(*
   FoldArithAdd - check arithmetic addition for constant folding.
*)

PROCEDURE FoldArithAdd (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary (tokenno, p, BuildAdd, quad, op1, op2, op3)
   END
END FoldArithAdd ;


(*
   CodeAddChecked - code an addition instruction, determine whether checking
                    is required.
*)

PROCEDURE CodeAddChecked (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF MustCheckOverflow (quad)
   THEN
      CodeAddCheck (quad, left, right)
   ELSE
      CodeAdd (quad, left, right)
   END
END CodeAddChecked ;


(*
   CodeAddCheck - encode addition but check for overflow.
*)

PROCEDURE CodeAddCheck (quad, left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinaryCheck (BuildAddCheck, quad)
   END
END CodeAddCheck ;


(*
   CodeAdd - encode addition.
*)

PROCEDURE CodeAdd (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildAdd, quad)
   END
END CodeAdd ;


(*
   FoldSub - check subtraction for constant folding.
*)

PROCEDURE FoldSub (tokenno: CARDINAL; p: WalkAction;
                   quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildSub, quad, op1, op2, op3)
   END
END FoldSub ;


(*
   CodeSubChecked - code a subtract instruction, determine whether checking
                    is required.
*)

PROCEDURE CodeSubChecked (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF MustCheckOverflow (quad)
   THEN
      CodeSubCheck (quad, left, right)
   ELSE
      CodeSub (quad, left, right)
   END
END CodeSubChecked ;


(*
   CodeSubCheck - encode subtraction but check for overflow.
*)

PROCEDURE CodeSubCheck (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinaryCheck (BuildSubCheck, quad)
   END
END CodeSubCheck ;


(*
   CodeSub - encode subtraction.
*)

PROCEDURE CodeSub (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildSub, quad)
   END
END CodeSub ;


(*
   FoldMult - check multiplication for constant folding.
*)

PROCEDURE FoldMult (tokenno: CARDINAL; p: WalkAction;
                    quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildMult, quad, op1, op2, op3)
   END
END FoldMult ;


(*
   CodeMultChecked - code a multiplication instruction, determine whether checking
                     is required.
*)

PROCEDURE CodeMultChecked (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF MustCheckOverflow (quad)
   THEN
      CodeMultCheck (quad, left, right)
   ELSE
      CodeMult (quad, left, right)
   END
END CodeMultChecked ;


(*
   CodeMultCheck - encode multiplication but check for overflow.
*)

PROCEDURE CodeMultCheck (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinaryCheck (BuildMultCheck, quad)
   END
END CodeMultCheck ;


(*
   CodeMult - encode multiplication.
*)

PROCEDURE CodeMult (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildMult, quad)
   END
END CodeMult ;


(*
   CodeDivM2Checked - code a divide instruction, determine whether checking
                      is required.
*)

PROCEDURE CodeDivM2Checked (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF MustCheckOverflow (quad)
   THEN
      CodeDivM2Check (quad, left, right)
   ELSE
      CodeDivM2 (quad, left, right)
   END
END CodeDivM2Checked ;


(*
   CodeDivM2Check - encode addition but check for overflow.
*)

PROCEDURE CodeDivM2Check (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinaryCheck (BuildDivM2Check, quad)
   END
END CodeDivM2Check ;


(*
   CodeModM2Checked - code a modulus instruction, determine whether checking
                      is required.
*)

PROCEDURE CodeModM2Checked (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF MustCheckOverflow (quad)
   THEN
      CodeModM2Check (quad, left, right)
   ELSE
      CodeModM2 (quad, left, right)
   END
END CodeModM2Checked ;


(*
   CodeModM2Check - encode addition but check for overflow.
*)

PROCEDURE CodeModM2Check (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinaryCheck (BuildModM2Check, quad)
   END
END CodeModM2Check ;


(*
   BinaryOperandRealFamily -
*)

PROCEDURE BinaryOperandRealFamily (op: CARDINAL) : BOOLEAN ;
VAR
   t: CARDINAL ;
BEGIN
   t := SkipType(GetType(op)) ;
   RETURN( IsComplexType(t) OR IsComplexN(t) OR
           IsRealType(t) OR IsRealN(t) )
END BinaryOperandRealFamily ;


(*
   FoldDivM2 - check division for constant folding.
*)

PROCEDURE FoldDivM2 (tokenno: CARDINAL; p: WalkAction;
                     quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      IF BinaryOperandRealFamily(op2) OR BinaryOperandRealFamily(op3)
      THEN
         FoldBinary(tokenno, p, BuildRDiv, quad, op1, op2, op3)
      ELSE
         FoldBinary(tokenno, p, BuildDivM2, quad, op1, op2, op3)
      END
   END
END FoldDivM2 ;


(*
   CodeDivM2 - encode division.
*)

PROCEDURE CodeDivM2 (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      IF BinaryOperandRealFamily (left) OR BinaryOperandRealFamily (right)
      THEN
         CodeBinary (BuildRDiv, quad)
      ELSE
         CodeBinary (BuildDivM2, quad)
      END
   END
END CodeDivM2 ;


(*
   FoldModM2 - check modulus for constant folding.
*)

PROCEDURE FoldModM2 (tokenno: CARDINAL; p: WalkAction;
                     quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildModM2, quad, op1, op2, op3)
   END
END FoldModM2 ;


(*
   CodeModM2 - encode modulus.
*)

PROCEDURE CodeModM2 (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildModM2, quad)
   END
END CodeModM2 ;


(*
   FoldDivTrunc - check division for constant folding.
*)

PROCEDURE FoldDivTrunc (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      IF BinaryOperandRealFamily(op2) OR BinaryOperandRealFamily(op3)
      THEN
         FoldBinary(tokenno, p, BuildRDiv, quad, op1, op2, op3)
      ELSE
         FoldBinary(tokenno, p, BuildDivTrunc, quad, op1, op2, op3)
      END
   END
END FoldDivTrunc ;


(*
   CodeDivTrunc - encode multiplication.
*)

PROCEDURE CodeDivTrunc (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      IF BinaryOperandRealFamily (left) OR BinaryOperandRealFamily (right)
      THEN
         CodeBinary (BuildRDiv, quad)
      ELSE
         CodeBinary (BuildDivTrunc, quad)
      END
   END
END CodeDivTrunc ;


(*
   FoldModTrunc - check modulus for constant folding.
*)

PROCEDURE FoldModTrunc (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildModTrunc, quad, op1, op2, op3)
   END
END FoldModTrunc ;


(*
   CodeModTrunc - encode modulus.
*)

PROCEDURE CodeModTrunc (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildModTrunc, quad)
   END
END CodeModTrunc ;


(*
   FoldDivCeil - check division for constant folding.
*)

PROCEDURE FoldDivCeil (tokenno: CARDINAL; p: WalkAction;
                       quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      IF BinaryOperandRealFamily(op2) OR BinaryOperandRealFamily(op3)
      THEN
         FoldBinary(tokenno, p, BuildRDiv, quad, op1, op2, op3)
      ELSE
         FoldBinary(tokenno, p, BuildDivCeil, quad, op1, op2, op3)
      END
   END
END FoldDivCeil ;


(*
   CodeDivCeil - encode multiplication.
*)

PROCEDURE CodeDivCeil (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      IF BinaryOperandRealFamily (left) OR BinaryOperandRealFamily (right)
      THEN
         CodeBinary (BuildRDiv, quad)
      ELSE
         CodeBinary (BuildDivCeil, quad)
      END
   END
END CodeDivCeil ;


(*
   FoldModCeil - check modulus for constant folding.
*)

PROCEDURE FoldModCeil (tokenno: CARDINAL; p: WalkAction;
                       quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildModCeil, quad, op1, op2, op3)
   END
END FoldModCeil ;


(*
   CodeModCeil - encode multiplication.
*)

PROCEDURE CodeModCeil (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildModCeil, quad)
   END
END CodeModCeil ;


(*
   FoldDivFloor - check division for constant folding.
*)

PROCEDURE FoldDivFloor (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      IF BinaryOperandRealFamily(op2) OR BinaryOperandRealFamily(op3)
      THEN
         FoldBinary(tokenno, p, BuildRDiv, quad, op1, op2, op3)
      ELSE
         FoldBinary(tokenno, p, BuildDivFloor, quad, op1, op2, op3)
      END
   END
END FoldDivFloor ;


(*
   CodeDivFloor - encode multiplication.
*)

PROCEDURE CodeDivFloor (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      IF BinaryOperandRealFamily (left) OR BinaryOperandRealFamily (right)
      THEN
         CodeBinary (BuildRDiv, quad)
      ELSE
         CodeBinary (BuildDivFloor, quad)
      END
   END
END CodeDivFloor ;


(*
   FoldModFloor - check modulus for constant folding.
*)

PROCEDURE FoldModFloor (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, op2, op3)
   THEN
      FoldBinary(tokenno, p, BuildModFloor, quad, op1, op2, op3)
   END
END FoldModFloor ;


(*
   CodeModFloor - encode modulus.
*)

PROCEDURE CodeModFloor (quad: CARDINAL; left, right: CARDINAL) ;
BEGIN
   IF BinaryOperands (quad, left, right)
   THEN
      CodeBinary (BuildModFloor, quad)
   END
END CodeModFloor ;


(*
   FoldBuiltinConst -
*)

PROCEDURE FoldBuiltinConst (tokenno: CARDINAL; p: WalkAction;
                            quad: CARDINAL; result, constDesc: CARDINAL) ;
VAR
   value: tree ;
BEGIN
   value := GetBuiltinConst (KeyToCharStar (Name (constDesc))) ;
   IF value = NIL
   THEN
      MetaErrorT1 (tokenno, 'unknown built in constant {%1Ead}', constDesc)
   ELSE
      AddModGcc (result, value) ;
      p (result) ;
      NoChange := FALSE ;
      SubQuad (quad)
   END
END FoldBuiltinConst ;


(*
   FoldBuiltinTypeInfo - attempts to fold a builtin attribute value on type op2.
*)

PROCEDURE FoldBuiltinTypeInfo (tokenno: CARDINAL; p: WalkAction;
                               quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   t       : tree ;
   location: location_t ;
BEGIN
   IF GccKnowsAbout(op2) AND CompletelyResolved(op2)
   THEN
      location := TokenToLocation(tokenno) ;
      t := GetBuiltinTypeInfo(location, Mod2Gcc(op2), KeyToCharStar(Name(op3))) ;
      IF t=NIL
      THEN
         MetaErrorT2 (tokenno, 'unknown built in constant {%1Ead} attribute for type {%2ad}', op3, op2)
      ELSE
         AddModGcc(op1, t) ;
         p(op1) ;
         NoChange := FALSE ;
         SubQuad(quad)
      END
   END
END FoldBuiltinTypeInfo ;


(*
   FoldTBitsize - attempt to fold the standard function SYSTEM.TBITSIZE
                  quadruple.  If the quadruple is folded it is removed.
*)

PROCEDURE FoldTBitsize  (tokenno: CARDINAL; p: WalkAction;
                         quad: CARDINAL;
                         res, type: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   TryDeclareType (type) ;
   type := GetDType (type) ;
   IF CompletelyResolved (type)
   THEN
      AddModGcc (res, BuildSystemTBitSize (location, Mod2Gcc (type))) ;
      p (res) ;
      NoChange := FALSE ;
      SubQuad (quad)
   END
END FoldTBitsize ;


(*
   FoldStandardFunction - attempts to fold a standard function.
*)

PROCEDURE FoldStandardFunction (tokenno: CARDINAL; p: WalkAction;
                                quad: CARDINAL;
                                op1, op2, op3: CARDINAL) ;
VAR
   s       : String ;
   type,
   d,
   result  : CARDINAL ;
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   IF GetSymName(op2)=MakeKey('Length')
   THEN
      TryDeclareConstant(tokenno, op3) ;
      IF IsConst(op3) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            IF IsConstString(op3)
            THEN
               AddModGcc(op1, FindSize(tokenno, op3)) ;
               p(op1) ;
               NoChange := FALSE ;
               SubQuad(quad)
            ELSE
               MetaErrorT1 (tokenno, 'parameter to LENGTH must be a string {%1Ead}', op3)
            END
         ELSE
            (* rewrite the quad to use becomes.  *)
            d := GetStringLength (tokenno, op3) ;
            s := Sprintf1 (Mark (InitString ("%d")), d) ;
            result := MakeConstLit (tokenno, makekey (string (s)), Cardinal) ;
            s := KillString (s) ;
            TryDeclareConstant (tokenno, result) ;
            PutQuad (quad, BecomesOp, op1, NulSym, result)
         END
      END
   ELSIF GetSymName(op2)=MakeKey('CAP')
   THEN
      TryDeclareConstant(tokenno, op3) ;
      IF IsConst(op3) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            IF (IsConstString(op3) AND (GetStringLength (tokenno, op3) = 1)) OR
               (GetType(op3)=Char)
            THEN
               AddModGcc(op1, BuildCap(location, Mod2Gcc(op3))) ;
               p(op1) ;
               NoChange := FALSE ;
               SubQuad(quad)
            ELSE
               MetaErrorT1 (tokenno, 'parameter to CAP must be a single character {%1Ead}', op3)
            END
         END
      END
   ELSIF GetSymName(op2)=MakeKey('ABS')
   THEN
      TryDeclareConstant(tokenno, op3) ;
      IF IsConst(op3) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            AddModGcc(op1, BuildAbs(location, Mod2Gcc(op3))) ;
            p(op1) ;
            NoChange := FALSE ;
            SubQuad(quad)
         END
      END
   ELSIF op2=Im
   THEN
      TryDeclareConstant(tokenno, op3) ;
      IF IsConst(op3) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            AddModGcc(op1, BuildIm(Mod2Gcc(op3))) ;
            p(op1) ;
            NoChange := FALSE ;
            SubQuad(quad)
         END
      END
   ELSIF op2=Re
   THEN
      TryDeclareConstant(tokenno, op3) ;
      IF IsConst(op3) AND GccKnowsAbout(op3)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            AddModGcc(op1, BuildRe(Mod2Gcc(op3))) ;
            p(op1) ;
            NoChange := FALSE ;
            SubQuad(quad)
         END
      END
   ELSIF op2=Cmplx
   THEN
      TryDeclareConstant(tokenno, GetNth(op3, 1)) ;
      TryDeclareConstant(tokenno, GetNth(op3, 2)) ;
      IF IsConst(GetNth(op3, 1)) AND GccKnowsAbout(GetNth(op3, 1)) AND
         IsConst(GetNth(op3, 2)) AND GccKnowsAbout(GetNth(op3, 2))
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst(op1)
         THEN
            type := GetCmplxReturnType(GetType(GetNth(op3, 1)), GetType(GetNth(op3, 2))) ;
            IF type=NulSym
            THEN
               MetaErrorT2 (tokenno, 'real {%1Eatd} and imaginary {%2atd} types are incompatible',
                            GetNth(op3, 1), GetNth(op3, 2))
            ELSE
               AddModGcc(op1, BuildCmplx(location,
                                         Mod2Gcc(type),
                                         Mod2Gcc(GetNth(op3, 1)),
                                         Mod2Gcc(GetNth(op3, 2)))) ;
               p(op1) ;
               NoChange := FALSE ;
               SubQuad(quad)
            END
         END
      END
   ELSIF op2=TBitSize
   THEN
      FoldTBitsize (tokenno, p, quad, op1, op3)
   ELSE
      InternalError ('only expecting LENGTH, CAP, ABS, IM, RE')
   END
END FoldStandardFunction ;


(*
   CodeStandardFunction -
*)

PROCEDURE CodeStandardFunction (quad: CARDINAL; result, function, param: CARDINAL) ;
VAR
   type    : CARDINAL ;
   location: location_t ;
BEGIN
   DeclareConstant (CurrentQuadToken, param) ;
   DeclareConstructor (CurrentQuadToken, quad, param) ;
   location := TokenToLocation (CurrentQuadToken) ;

   IF (function # NulSym) AND (GetSymName (function) = MakeKey ('Length'))
   THEN
      IF IsConst (result)
      THEN
         InternalError ('LENGTH function should already have been folded')
      END
   ELSIF (function # NulSym) AND (GetSymName (function) = MakeKey ('CAP'))
   THEN
      IF IsConst (result)
      THEN
         InternalError ('CAP function should already have been folded')
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), BuildCap (location, Mod2Gcc (param)))
      END
   ELSIF (function # NulSym) AND (GetSymName (function) = MakeKey('ABS'))
   THEN
      IF IsConst (result)
      THEN
         InternalError ('ABS function should already have been folded')
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), BuildAbs (location, Mod2Gcc (param)))
      END
   ELSIF function = Im
   THEN
      IF IsConst (result)
      THEN
         InternalError ('IM function should already have been folded')
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), BuildIm (Mod2Gcc (param)))
      END
   ELSIF function = Re
   THEN
      IF IsConst (result)
      THEN
         InternalError ('RE function should already have been folded')
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), BuildRe (Mod2Gcc (param)))
      END
   ELSIF function = Cmplx
   THEN
      IF IsConst (result)
      THEN
         InternalError ('CMPLX function should already have been folded')
      ELSE
         type := GetCmplxReturnType (GetType (GetNth (param, 1)), GetType (GetNth (param, 2))) ;
         IF type = NulSym
         THEN
            MetaErrorT2 (CurrentQuadToken,
                         'real {%1Eatd} and imaginary {%2atd} types are incompatible',
                         GetNth (param, 1), GetNth (param, 2))
         ELSE
            BuildAssignmentStatement (location, Mod2Gcc (result), BuildCmplx(location,
                                                                             Mod2Gcc (type),
                                                                             Mod2Gcc (GetNth (param, 1)),
                                                                             Mod2Gcc (GetNth (param, 2))))
         END
      END
   ELSIF function = TBitSize
   THEN
      IF IsConst (result)
      THEN
         InternalError ('TBITSIZE function should already have been folded')
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), BuildTBitSize (location, Mod2Gcc (param)))
      END
   ELSE
      InternalError ('expecting LENGTH, CAP, ABS, IM')
   END
END CodeStandardFunction ;


(*
   CodeSavePriority - checks to see whether op2 is reachable and is directly accessible
                      externally. If so then it saves the current interrupt priority
                      in op1 and sets the current priority to that determined by
                      appropriate module.

                      op1 := op3(GetModuleScope(op2))
*)

PROCEDURE CodeSavePriority (oldValue, scopeSym, procedureSym: CARDINAL) ;
VAR
   funcTree: tree ;
   mod     : CARDINAL ;
   n       : Name ;
   location: location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsModule (scopeSym) OR IsDefImp (scopeSym) OR
      (IsProcedure (scopeSym) AND GetNeedSavePriority (scopeSym))
   THEN
      IF IsProcedure (scopeSym)
      THEN
         mod := GetModuleScope (scopeSym) ;
      ELSE
         Assert (IsModule(scopeSym) OR IsDefImp (scopeSym)) ;
         mod := scopeSym
      END ;
      IF GetPriority (mod) # NulSym
      THEN
         IF PriorityDebugging
         THEN
            n := GetSymName (scopeSym) ;
            printf1 ('procedure <%a> needs to save interrupts\n', n)
         END ;
         DeclareConstant (CurrentQuadToken, GetPriority (mod)) ;
         BuildParam (location, Mod2Gcc (GetPriority (mod))) ;
         funcTree := BuildProcedureCallTree (location, Mod2Gcc (procedureSym), Mod2Gcc (GetType (procedureSym))) ;
         funcTree := BuildFunctValue (location, Mod2Gcc (oldValue)) ;
         AddStatement (location, funcTree)
      END
   END
END CodeSavePriority ;


(*
   CodeRestorePriority - checks to see whether op2 is reachable and is directly accessible
                         externally. If so then it restores the previous interrupt priority
                         held in op1.

                         op1 := op3(op1)
*)

PROCEDURE CodeRestorePriority (oldValue, scopeSym, procedureSym: CARDINAL) ;
VAR
   funcTree: tree ;
   mod     : CARDINAL ;
   n       : Name ;
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;

   IF IsModule (scopeSym) OR IsDefImp (scopeSym) OR
      (IsProcedure (scopeSym) AND GetNeedSavePriority (scopeSym))
   THEN
      IF IsProcedure (scopeSym)
      THEN
         mod := GetModuleScope (scopeSym) ;
      ELSE
         Assert (IsModule (scopeSym) OR IsDefImp (scopeSym)) ;
         mod := scopeSym
      END ;
      IF GetPriority (mod) # NulSym
      THEN
         IF PriorityDebugging
         THEN
            n := GetSymName (scopeSym) ;
            printf1 ('procedure <%a> needs to restore interrupts\n', n)
         END ;
         BuildParam (location, Mod2Gcc (oldValue)) ;
         funcTree := BuildProcedureCallTree (location, Mod2Gcc (procedureSym), Mod2Gcc (GetType (procedureSym))) ;
         funcTree := BuildFunctValue (location, Mod2Gcc (oldValue)) ;
         AddStatement(location, funcTree)
      END
   END
END CodeRestorePriority ;


(*
   FoldBinarySet - attempts to fold set arithmetic it removes the quad if successful.
*)

PROCEDURE FoldBinarySet (tokenno: CARDINAL; p: WalkAction; op: DoProcedure;
                         quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   (* firstly try and ensure that constants are declared *)
   TryDeclareConstant(tokenno, op2) ;
   TryDeclareConstant(tokenno, op3) ;
   location := TokenToLocation(tokenno) ;

   IF GccKnowsAbout(op2) AND GccKnowsAbout(op3)
   THEN
      IF CheckBinaryExpressionTypes (quad, p)
      THEN
         IF IsConst(op2) AND IsConstSet(op2) AND
            IsConst(op3) AND IsConstSet(op3) AND
            IsConst(op1)
         THEN
            IF IsValueSolved(op2) AND IsValueSolved(op3)
            THEN
               Assert(MixTypes(FindType(op3), FindType(op2), tokenno)#NulSym) ;
               PutConst(op1, MixTypes(FindType(op3), FindType(op2), tokenno)) ;
               PushValue(op2) ;
               PushValue(op3) ;
               op(tokenno) ;
               PopValue(op1) ;
               PushValue(op1) ;
               PutConstSet(op1) ;
               AddModGcc(op1,
                         DeclareKnownConstant(location,
                                              Mod2Gcc(GetType(op3)),
                                              PopSetTree(tokenno))) ;
               p(op1) ;
               NoChange := FALSE ;
               SubQuad(quad)
            END
         END
      END
   END
END FoldBinarySet ;


(*
   FoldSetOr - check whether we can fold a set arithmetic or.
*)

PROCEDURE FoldSetOr (tokenno: CARDINAL; p: WalkAction;
                     quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet (tokenno, p, SetOr, quad, op1, op2, op3)
END FoldSetOr ;


(*
   CodeSetOr - encode set arithmetic or.
*)

PROCEDURE CodeSetOr (quad: CARDINAL) ;
BEGIN
   CodeBinarySet (BuildLogicalOr, SetOr, quad)
END CodeSetOr ;


(*
   FoldSetAnd - check whether we can fold a logical and.
*)

PROCEDURE FoldSetAnd (tokenno: CARDINAL; p: WalkAction;
                      quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet(tokenno, p, SetAnd, quad, op1, op2, op3)
END FoldSetAnd ;


(*
   CodeSetAnd - encode set arithmetic and.
*)

PROCEDURE CodeSetAnd (quad: CARDINAL) ;
BEGIN
   CodeBinarySet (BuildLogicalAnd, SetAnd, quad)
END CodeSetAnd ;


(*
   CodeBinarySetShift - encode a binary set arithmetic operation.
                        The set maybe larger than a machine word
                        and the value of one word may effect the
                        values of another - ie shift and rotate.
                        Set sizes of a word or less are evaluated
                        with binop, whereas multiword sets are
                        evaluated by M2RTS.
*)

PROCEDURE CodeBinarySetShift (binop: BuildSetProcedure;
                              doOp : DoProcedure;
                              var, left, right: Name;
                              quad: CARDINAL;
                              op1, op2, op3: CARDINAL) ;
VAR
   nBits,
   unbounded,
   leftproc,
   rightproc,
   varproc  : tree ;
   location : location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   DeclareConstant(CurrentQuadToken, op3) ;
   DeclareConstant(CurrentQuadToken, op2) ;
   DeclareConstructor(CurrentQuadToken, quad, op3) ;
   DeclareConstructor(CurrentQuadToken, quad, op2) ;
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1)
   THEN
      IF IsValueSolved(op2) AND IsValueSolved(op3)
      THEN
         Assert(MixTypes(FindType(op3),
                         FindType(op2), CurrentQuadToken)#NulSym) ;
         PutConst(op1, FindType(op3)) ;
         PushValue(op2) ;
         PushValue(op3) ;
         doOp(CurrentQuadToken) ;
         PopValue(op1) ;
         PutConstSet(op1)
      ELSE
         MetaErrorT0 (CurrentQuadToken, '{%E}constant expression cannot be evaluated')
      END
   ELSE
      varproc := Mod2Gcc(FromModuleGetSym(CurrentQuadToken, var, System)) ;
      leftproc := Mod2Gcc(FromModuleGetSym(CurrentQuadToken, left, System)) ;
      rightproc := Mod2Gcc(FromModuleGetSym(CurrentQuadToken, right, System)) ;
      unbounded := Mod2Gcc(GetType(GetNthParamAny (FromModuleGetSym(CurrentQuadToken,
                                                                var, System), 1))) ;
      PushValue(GetTypeMax(SkipType(GetType(op1)))) ;
      PushIntegerTree(BuildConvert(location, GetM2ZType(), PopIntegerTree(), FALSE)) ;

      PushValue(GetTypeMin(SkipType(GetType(op1)))) ;
      PushIntegerTree(BuildConvert(location, GetM2ZType(), PopIntegerTree(), FALSE)) ;
      Sub ;
      PushCard(1) ;
      PushIntegerTree(BuildConvert(location, GetM2ZType(), PopIntegerTree(), FALSE)) ;
      Addn ;
      nBits := PopIntegerTree() ;
      BuildBinarySetDo(location,
                       Mod2Gcc(SkipType(GetType(op1))),
                       Mod2Gcc(op1),
                       Mod2Gcc(op2),
                       Mod2Gcc(op3),
                       binop,
                       GetMode(op1)=LeftValue,
                       GetMode(op2)=LeftValue,
                       GetMode(op3)=LeftValue,
                       nBits,
                       unbounded,
                       varproc, leftproc, rightproc)
   END
END CodeBinarySetShift ;


(*
   FoldSetShift - check whether we can fold a logical shift.
*)

PROCEDURE FoldSetShift (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet(tokenno, p, SetShift, quad, op1, op2, op3)
END FoldSetShift ;


(*
   CodeSetShift - encode set arithmetic shift.
*)

PROCEDURE CodeSetShift (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   CodeBinarySetShift (BuildLogicalShift,
                       SetShift,
                       MakeKey('ShiftVal'),
                       MakeKey('ShiftLeft'),
                       MakeKey('ShiftRight'),
                       quad, op1, op2, op3)
END CodeSetShift ;


(*
   FoldSetRotate - check whether we can fold a logical rotate.
*)

PROCEDURE FoldSetRotate (tokenno: CARDINAL; p: WalkAction;
                        quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet(tokenno, p, SetRotate, quad, op1, op2, op3)
END FoldSetRotate ;


(*
   CodeSetRotate - encode set arithmetic rotate.
*)

PROCEDURE CodeSetRotate (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   CodeBinarySetShift (BuildLogicalRotate,
                       SetRotate,
                       MakeKey ('RotateVal'),
                       MakeKey ('RotateLeft'),
                       MakeKey ('RotateRight'),
                       quad, op1, op2, op3)
END CodeSetRotate ;


(*
   FoldSetLogicalDifference - check whether we can fold a logical difference.
*)

(*
PROCEDURE FoldSetLogicalDifference (tokenno: CARDINAL; p: WalkAction;
                                    quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet(tokenno, p, SetDifference, quad, op1, op2, op3)
END FoldSetLogicalDifference ;
*)


(*
   CodeSetLogicalDifference - encode set arithmetic logical difference.
*)

PROCEDURE CodeSetLogicalDifference (quad: CARDINAL) ;
BEGIN
   CodeBinarySet (BuildLogicalDifference, SetDifference, quad)
END CodeSetLogicalDifference ;


(*
   FoldSymmetricDifference - check whether we can fold a logical difference.
*)

PROCEDURE FoldSymmetricDifference (tokenno: CARDINAL; p: WalkAction;
                                   quad: CARDINAL; op1, op2, op3: CARDINAL) ;
BEGIN
   FoldBinarySet (tokenno, p, SetSymmetricDifference, quad, op1, op2, op3)
END FoldSymmetricDifference ;


(*
   CodeSetSymmetricDifference - code set difference.
*)

PROCEDURE CodeSetSymmetricDifference (quad: CARDINAL) ;
BEGIN
   CodeBinarySet (BuildSymmetricDifference, SetSymmetricDifference, quad)
END CodeSetSymmetricDifference ;


(*
   CodeUnarySet - encode a unary set arithmetic operation.
                  Set operands may be longer than a word.
*)

PROCEDURE CodeUnarySet (unop: BuildUnarySetFunction; constop: DoUnaryProcedure;
                        quad: CARDINAL; result, expr: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   DeclareConstant (CurrentQuadToken, expr) ;
   DeclareConstructor (CurrentQuadToken, quad, expr) ;
   location := TokenToLocation (CurrentQuadToken) ;

   IF IsConst (result)
   THEN
      IF IsValueSolved (expr)
      THEN
         Assert (FindType (expr) # NulSym) ;
         PutConst (result, FindType (expr)) ;
         PushValue (expr) ;
         constop (CurrentQuadToken) ;
         PopValue (result) ;
         PushValue (result) ;
         PutConstSet (result) ;
         ConstantKnownAndUsed (result,
                               DeclareKnownConstant(location,
                                                    Mod2Gcc (GetType (expr)),
                                                    PopSetTree (CurrentQuadToken)))
      ELSE
         MetaErrorT0 (CurrentQuadToken,
                      '{%E}constant expression cannot be evaluated')
      END
   ELSE
      checkDeclare (result) ;
      BuildUnaryForeachWordDo (location,
                               Mod2Gcc (GetType (result)), Mod2Gcc (result), Mod2Gcc (expr), unop,
                               GetMode(result) = LeftValue, GetMode(expr) = LeftValue,
                               IsConst (result), IsConst (expr))
   END
END CodeUnarySet ;


(*
   FoldIncl - check whether we can fold the InclOp.
              result := result + (1 << expr)
*)

PROCEDURE FoldIncl (tokenno: CARDINAL; p: WalkAction;
                    quad: CARDINAL; result, expr: CARDINAL) ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   TryDeclareConstant (tokenno, expr) ;
   IF IsConst (result) AND IsConst (expr)
   THEN
      IF GccKnowsAbout (expr) AND IsValueSolved (result)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         PushValue (result) ;
         AddBit (tokenno, expr) ;
         AddModGcc (result, PopSetTree(tokenno)) ;
         p (result) ;
         NoChange := FALSE ;
         SubQuad (quad)
      END
   END
END FoldIncl ;


(*
   FoldIfLess - check to see if it is possible to evaluate
                if op1 < op2 then goto op3.
*)

PROCEDURE FoldIfLess (tokenno: CARDINAL;
                      quad: CARDINAL; left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF Less (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfLess ;


(*
   FoldIfGre - check to see if it is possible to evaluate
               if op1 > op2 then goto op3.
*)

PROCEDURE FoldIfGre (tokenno: CARDINAL;
                     quad: CARDINAL;
                     left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF Gre (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfGre ;


(*
   FoldIfLessEqu - check to see if it is possible to evaluate
                   if op1 <= op2 then goto op3.
*)

PROCEDURE FoldIfLessEqu (tokenno: CARDINAL;
                         quad: CARDINAL;
                         left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF LessEqu (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfLessEqu ;


(*
   FoldIfGreEqu - check to see if it is possible to evaluate
                  if op1 >= op2 then goto op3.
*)

PROCEDURE FoldIfGreEqu (tokenno: CARDINAL;
                        quad: CARDINAL;
                        left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF GreEqu (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfGreEqu ;


(*
   FoldIfIn - check whether we can fold the IfInOp
              if op1 in op2 then goto op3
*)

PROCEDURE FoldIfIn (tokenno: CARDINAL;
                    quad: CARDINAL;
                    left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant (tokenno, left) ;
   TryDeclareConstant (tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         IF CheckBinaryExpressionTypes (quad, NoWalkProcedure)
         THEN
            (* We can take advantage of the known values and evaluate the condition.  *)
            PushValue (right) ;
            IF SetIn (tokenno, left)
            THEN
               PutQuad (quad, GotoOp, NulSym, NulSym, destQuad) ;
            ELSE
               SubQuad (quad)
            END
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfIn ;


(*
   FoldIfNotIn - check whether we can fold the IfNotInOp
                 if not (op1 in op2) then goto op3
*)

PROCEDURE FoldIfNotIn (tokenno: CARDINAL;
                       quad: CARDINAL;
                       left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant (tokenno, left) ;
   TryDeclareConstant (tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         IF CheckBinaryExpressionTypes (quad, NoWalkProcedure)
         THEN
            (* We can take advantage of the known values and evaluate the
               condition.  *)
            PushValue (right) ;
            IF NOT SetIn (tokenno, left)
            THEN
               PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
            ELSE
               SubQuad (quad)
            END
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfNotIn ;


(*
   FoldIfEqu - check to see if it is possible to evaluate
               if op1 = op2 then goto op3.
*)

PROCEDURE FoldIfEqu (tokenno: CARDINAL;
                     quad: CARDINAL;
                     left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the
            condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF Equ (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfEqu ;


(*
   FoldIfNotEqu - check to see if it is possible to evaluate
                  if op1 # op2 then goto op3.
*)

PROCEDURE FoldIfNotEqu (tokenno: CARDINAL;
                        quad: CARDINAL;
                        left, right, destQuad: CARDINAL) ;
BEGIN
   (* Firstly ensure that constant literals are declared.  *)
   TryDeclareConstant(tokenno, left) ;
   TryDeclareConstant(tokenno, right) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      IF IsValueSolved (left) AND IsValueSolved (right)
      THEN
         (* We can take advantage of the known values and evaluate the
            condition.  *)
         PushValue (left) ;
         PushValue (right) ;
         IF NotEqu (tokenno)
         THEN
            PutQuad (quad, GotoOp, NulSym, NulSym, destQuad)
         ELSE
            SubQuad (quad)
         END ;
         NoChange := FALSE
      END
   END
END FoldIfNotEqu ;


(*
   GetSetLimits - assigns low and high to the limits of the declared, set.
*)

PROCEDURE GetSetLimits (set: CARDINAL; VAR low, high: CARDINAL) ;
VAR
   type: CARDINAL ;
BEGIN
   type := GetType(set) ;
   IF IsSubrange(type)
   THEN
      GetSubrange(type, high, low) ;
   ELSE
      low := GetTypeMin(type) ;
      high := GetTypeMax(type)
   END
END GetSetLimits ;


(*
   GetFieldNo - returns the field number in the, set, which contains, element.
*)

PROCEDURE GetFieldNo (tokenno: CARDINAL; element: CARDINAL; set: CARDINAL; VAR offset: tree) : INTEGER ;
VAR
   low, high, bpw, c: CARDINAL ;
   location         : location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   bpw := GetBitsPerBitset() ;
   GetSetLimits(set, low, high) ;

   (* check element is legal *)

   PushValue(element) ;
   PushValue(low) ;
   IF Less(tokenno)
   THEN
      (* out of range *)
      RETURN( -1 )
   ELSE
      PushValue(element) ;
      PushValue(high) ;
      IF Gre(tokenno)
      THEN
         RETURN( -1 )
      END
   END ;

   (* all legal *)

   PushValue(low) ;
   offset := PopIntegerTree() ;
   c := 0 ;
   PushValue(element) ;
   PushValue(low) ;
   PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
   PushCard(bpw) ;
   PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
   Addn ;
   WHILE GreEqu(tokenno) DO
      INC(c) ;   (* move onto next field *)
      PushValue(element) ;
      PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
      PushCard((c+1)*bpw) ;
      PushValue(low) ;
      PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
      Addn ;
      PushIntegerTree(offset) ;
      PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
      PushCard(bpw) ;
      PushIntegerTree(ToCardinal(location, PopIntegerTree())) ;
      Addn ;
      offset := PopIntegerTree()
   END ;
   RETURN( VAL(INTEGER, c) )
END GetFieldNo ;


(*
   CodeIncl - encode an InclOp:
              result := result + (1 << expr)
*)

PROCEDURE CodeIncl (result, expr: CARDINAL) ;
VAR
   low,
   high    : CARDINAL ;
   offset  : tree ;
   fieldno : INTEGER ;
   location: location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   DeclareConstant (CurrentQuadToken, expr) ;
   location := TokenToLocation (CurrentQuadToken) ;

   IF IsConst (result)
   THEN
      IF IsConst (expr)
      THEN
         InternalError ('this quadruple should have been removed by FoldIncl')
      ELSE
         InternalError ('should not get to here (why are we generating <incl const, var> ?)')
      END
   ELSE
      IF IsConst (expr)
      THEN
         fieldno := GetFieldNo (CurrentQuadToken, expr, GetType (result), offset) ;
         IF fieldno >= 0
         THEN
            PushValue (expr) ;
            PushIntegerTree (offset) ;
            Sub ;
            BuildIncludeVarConst (location,
                                  Mod2Gcc (GetType (result)),
                                  Mod2Gcc (result),
                                  PopIntegerTree (),
                                  GetMode (result) = LeftValue, fieldno)
         ELSE
            MetaErrorT1 (CurrentQuadToken, 'bit exceeded the range of set {%1Eatd}', result)
         END
      ELSE
         GetSetLimits (GetType (result), low, high) ;
         BuildIncludeVarVar (location,
                             Mod2Gcc (GetType(result)),
                             Mod2Gcc (result), Mod2Gcc(expr), GetMode(result) = LeftValue, Mod2Gcc (low))
      END
   END
END CodeIncl ;


(*
   FoldExcl - check whether we can fold the InclOp.
              op1 := op1 - (1 << op3)
*)

PROCEDURE FoldExcl (tokenno: CARDINAL; p: WalkAction;
                    quad: CARDINAL; result, expr: CARDINAL) ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   TryDeclareConstant (tokenno, expr) ;
   IF IsConst (result) AND IsConst (expr)
   THEN
      IF GccKnowsAbout (expr) AND IsValueSolved (result)
      THEN
         PushValue (result) ;
         SubBit (tokenno, expr) ;
         AddModGcc (result, PopSetTree (tokenno)) ;
         p (result) ;
         NoChange := FALSE ;
         SubQuad(quad)
      END
   END
END FoldExcl ;


(*
   CodeExcl - encode an ExclOp:
              result := result - (1 << expr)
*)

PROCEDURE CodeExcl (result, expr: CARDINAL) ;
VAR
   low,
   high    : CARDINAL ;
   offset  : tree ;
   fieldno : INTEGER ;
   location: location_t ;
BEGIN
   (* firstly ensure that constant literals are declared *)
   DeclareConstant (CurrentQuadToken, expr) ;
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst (result)
   THEN
      InternalError ('should not get to here (if we do we should consider calling FoldInclOp)')
   ELSE
      IF IsConst (expr)
      THEN
         fieldno := GetFieldNo (CurrentQuadToken, expr, GetType (result), offset) ;
         IF fieldno >= 0
         THEN
            PushValue (expr) ;
            PushIntegerTree (offset) ;
            Sub ;
            BuildExcludeVarConst (location,
                                  Mod2Gcc (GetType (result)),
                                  Mod2Gcc (result), PopIntegerTree (),
                                  GetMode (result)=LeftValue, fieldno)
         ELSE
            MetaErrorT1 (CurrentQuadToken, 'bit exceeded the range of set {%1Eatd}', result)
         END
      ELSE
         GetSetLimits (GetType (result), low, high) ;
         BuildExcludeVarVar (location,
                             Mod2Gcc (GetType(result)),
                             Mod2Gcc (result), Mod2Gcc(expr), GetMode(result) = LeftValue, Mod2Gcc (low))
      END
   END
END CodeExcl ;


(*
   FoldUnary - check whether we can fold the unop operation.
*)

PROCEDURE FoldUnary (tokenno: CARDINAL; p: WalkAction;
                     unop: BuildUnaryProcedure; ZConstToTypedConst: tree;
                     quad: CARDINAL; result, expr: CARDINAL) ;
VAR
   tv      : tree ;
   location: location_t ;
BEGIN
   (* firstly ensure that any constant literal is declared *)
   TryDeclareConstant (tokenno, expr) ;
   location := TokenToLocation (tokenno) ;

   IF IsConst (expr)
   THEN
      IF GccKnowsAbout (expr)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst (result)
         THEN
            IF ZConstToTypedConst = tree(NIL)
            THEN
               IF (GetType (expr) = NulSym) OR IsOrdinalType (SkipType (GetType (expr)))
               THEN
                  ZConstToTypedConst := GetM2ZType ()
               ELSIF IsRealType (SkipType (GetType (expr))) OR IsRealN (SkipType (GetType (expr)))
               THEN
                  ZConstToTypedConst := GetM2RType ()
               ELSIF IsComplexType (SkipType (GetType (expr))) OR
                     IsComplexN (SkipType (GetType (expr)))
               THEN
                  ZConstToTypedConst := GetM2CType ()
               END
            END ;
            IF GetType(result) = NulSym
            THEN
               PutConst (result, NegateType (GetType (expr) (* , tokenno *) ))
            END ;
            tv := unop (location, LValueToGenericPtrOrConvert (expr, ZConstToTypedConst), FALSE) ;
            CheckOrResetOverflow (tokenno, tv, MustCheckOverflow (quad)) ;

            AddModGcc (result, DeclareKnownConstant (location, ZConstToTypedConst, tv)) ;
            p (result) ;
            NoChange := FALSE ;
            SubQuad (quad)
         ELSE
            (* we can still fold the expression, but not the assignment, however, we will
               not do this here but in CodeUnary
             *)
         END
      END
   END
END FoldUnary ;


(*
   FoldUnarySet - check whether we can fold the doOp operation.
*)

PROCEDURE FoldUnarySet (tokenno: CARDINAL; p: WalkAction; doOp: DoUnaryProcedure;
                        quad: CARDINAL; result, expr: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   (* firstly try and ensure that constants are declared *)
   TryDeclareConstant (tokenno, expr) ;
   location := TokenToLocation (tokenno) ;

   IF IsConst (expr) AND IsConstSet (expr) AND
      IsConst (result)
   THEN
      IF IsValueSolved (expr) AND (GetType (expr) # NulSym)
      THEN
         PutConst (result, FindType (expr)) ;
         PushValue (expr) ;
         doOp (tokenno) ;
         PopValue (result) ;
         PushValue (result) ;
         PutConstSet (result) ;
         AddModGcc (result,
                    DeclareKnownConstant (location,
                                          Mod2Gcc (GetType (expr)),
                                          PopSetTree (tokenno))) ;
         p (result) ;
         NoChange := FALSE ;
         SubQuad (quad)
      END
   END
END FoldUnarySet ;


(*
   CodeUnaryCheck - encode a unary arithmetic operation.
*)

PROCEDURE CodeUnaryCheck (unop: BuildUnaryCheckProcedure; ZConstToTypedConst: tree;
                          quad: CARDINAL; result, expr: CARDINAL) ;
VAR
   lowestType: CARDINAL ;
   min, max,
   lowest,
   tv        : tree ;
   location  : location_t ;
BEGIN
   (* firstly ensure that any constant literal is declared *)
   DeclareConstant(CurrentQuadToken, expr) ;
   DeclareConstructor(CurrentQuadToken, quad, expr) ;
   location := TokenToLocation(CurrentQuadToken) ;

   lowestType := GetLType (result) ;
   IF lowestType=NulSym
   THEN
      lowest := NIL ;
   ELSE
      lowest := Mod2Gcc (lowestType)
   END ;
   IF GetMinMax (CurrentQuadToken, lowestType, min, max)
   THEN
      tv := unop (location, LValueToGenericPtr (location, expr), lowest, min, max)
   ELSE
      tv := unop (location, LValueToGenericPtr (location, expr), NIL, NIL, NIL)
   END ;
   CheckOrResetOverflow (CurrentQuadToken, tv, MustCheckOverflow(quad)) ;
   IF IsConst (result)
   THEN
      IF ZConstToTypedConst = tree (NIL)
      THEN
         ZConstToTypedConst := tree (Mod2Gcc( GetType (expr)))
      END ;
      (* still have a constant which was not resolved, pass it to gcc *)
      PutConst (result, FindType (expr)) ;
      ConstantKnownAndUsed (result, DeclareKnownConstant (location, ZConstToTypedConst, tv))
   ELSE
      IF EnableSSA AND IsVariableSSA (result)
      THEN
         Replace (result, tv)
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), tv)
      END
   END
END CodeUnaryCheck ;


(*
   CodeUnary - encode a unary arithmetic operation.
*)

PROCEDURE CodeUnary (unop: BuildUnaryProcedure; ZConstToTypedConst: tree;
                     quad: CARDINAL; result, expr: CARDINAL) ;
VAR
   tv      : tree ;
   location: location_t ;
BEGIN
   (* firstly ensure that any constant literal is declared *)
   DeclareConstant (CurrentQuadToken, expr) ;
   DeclareConstructor (CurrentQuadToken, quad, expr) ;
   location := TokenToLocation (CurrentQuadToken) ;

   tv := unop(location, LValueToGenericPtr (location, expr), FALSE) ;
   CheckOrResetOverflow (CurrentQuadToken, tv, MustCheckOverflow (quad)) ;
   IF IsConst(result)
   THEN
      IF ZConstToTypedConst=tree(NIL)
      THEN
         ZConstToTypedConst := tree(Mod2Gcc(GetType(expr)))
      END ;
      (* still have a constant which was not resolved, pass it to gcc *)
      PutConst (result, FindType (expr)) ;
      ConstantKnownAndUsed (result, DeclareKnownConstant (location, ZConstToTypedConst, tv))
   ELSE
      IF EnableSSA AND IsVariableSSA (result)
      THEN
         Replace (result, tv)
      ELSE
         BuildAssignmentStatement (location, Mod2Gcc (result), tv)
      END
   END
END CodeUnary ;


(*
   FoldNegate - check unary negate for constant folding.
*)

PROCEDURE FoldNegate (tokenno: CARDINAL; p: WalkAction;
                      quad: CARDINAL; result, expr: CARDINAL) ;
BEGIN
   IF IsConstSet (expr)
   THEN
      FoldUnarySet (tokenno, p, SetNegate, quad, result, expr)
   ELSE
      FoldUnary (tokenno, p, BuildNegate, NIL, quad, result, expr)
   END
END FoldNegate ;


(*
   CodeNegateChecked - code a negate instruction, determine whether checking
                       is required.
*)

PROCEDURE CodeNegateChecked (quad: CARDINAL; op1, op3: CARDINAL) ;
BEGIN
   IF IsConstSet (op3) OR IsSet (GetType (op3))
   THEN
      CodeUnarySet (BuildSetNegate, SetNegate, quad, op1, op3)
   ELSIF UnaryOperand (quad, op3)
   THEN
      IF MustCheckOverflow (quad)
      THEN
         CodeUnaryCheck (BuildNegateCheck, NIL, quad, op1, op3)
      ELSE
         CodeUnary (BuildNegate, NIL, quad, op1, op3)
      END
   END
END CodeNegateChecked ;


(*
   FoldSize - check unary SIZE for constant folding.
*)

PROCEDURE FoldSize (tokenno: CARDINAL; p: WalkAction;
                    quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   t       : tree ;
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   IF IsConst(op1) AND CompletelyResolved(op3)
   THEN
      IF op2=NulSym
      THEN
         t := BuildSize(location, Mod2Gcc(op3), FALSE) ;
         PushIntegerTree(t) ;
         PopValue(op1) ;
         PutConst(op1, Cardinal) ;
         p(op1) ;
         NoChange := FALSE ;
         SubQuad(quad) ;
         t := RememberConstant(t)
      ELSIF GccKnowsAbout(op2)
      THEN
         (* ignore the chosen varients as we implement it as a C union *)
         t := BuildSize(location, Mod2Gcc(op3), FALSE) ;
         PushIntegerTree(t) ;
         PopValue(op1) ;
         PutConst(op1, Cardinal) ;
         p(op1) ;
         NoChange := FALSE ;
         SubQuad(quad) ;
         t := RememberConstant(t)
      END
   END
END FoldSize ;


(*
   CodeSize - encode the inbuilt SIZE function.
*)

PROCEDURE CodeSize (result, sym: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   PushIntegerTree (BuildSize (location, Mod2Gcc (sym), FALSE)) ;
   IF IsConst (result)
   THEN
      PopValue (result) ;
      PutConst (result, Cardinal) ;
      PushValue (result) ;
      ConstantKnownAndUsed (result,
                            DeclareKnownConstant (location,
                                                  GetIntegerType (),
                                                  PopIntegerTree ()))
   ELSE
      BuildAssignmentStatement (location, Mod2Gcc (result), PopIntegerTree ())
   END
END CodeSize ;


(*
   FoldRecordField - check whether we can fold an RecordFieldOp quadruple.
                     Very similar to FoldBinary, except that we need to
                     hard code a few parameters to the gcc backend.
*)

PROCEDURE FoldRecordField (tokenno: CARDINAL; p: WalkAction;
                           quad: CARDINAL; result, record, field: CARDINAL) ;
VAR
   recordType,
   fieldType : CARDINAL ;
   ptr       : tree ;
   location  : location_t ;
BEGIN
   RETURN ;  (* this procedure should no longer be called *)

   location := TokenToLocation(tokenno) ;
   (* firstly ensure that any constant literal is declared *)
   TryDeclareConstant(tokenno, record) ;
   IF IsRecordField(record) OR IsFieldVarient(record)
   THEN
      recordType := GetType (record) ;
      fieldType := GetType (field) ;
      IF GccKnowsAbout (record) AND GccKnowsAbout (field) AND
         GccKnowsAbout (recordType) AND GccKnowsAbout (fieldType) AND
         CompletelyResolved (recordType) AND CompletelyResolved (fieldType)
      THEN
         (* fine, we can take advantage of this and fold constants *)
         IF IsConst (result)
         THEN
            ptr := BuildComponentRef (location, Mod2Gcc (record), Mod2Gcc (field)) ;
            IF NOT IsValueSolved (result)
            THEN
               PushIntegerTree (ptr) ;
               PopValue (result)
            END ;
            PutConst (result, fieldType) ;
            AddModGcc (result, DeclareKnownConstant (location, Mod2Gcc (fieldType), ptr)) ;
            p (result) ;
            NoChange := FALSE ;
            SubQuad (quad)
         ELSE
            (* we can still fold the expression, but not the assignment, however, we will
               not do this here but in CodeOffset
             *)
         END
      END
   END
END FoldRecordField ;


(*
   CodeRecordField - encode a reference to a field within a record.
*)

PROCEDURE CodeRecordField (result, record, field: CARDINAL) ;
VAR
   recordType,
   fieldType : CARDINAL ;
   ptr       : tree ;
   location  : location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   (* firstly ensure that any constant literal is declared *)
   IF IsRecordField (field) OR IsFieldVarient (field)
   THEN
      recordType := GetType (record) ;
      fieldType := GetType (field) ;
      IF GccKnowsAbout (record) AND GccKnowsAbout (field) AND
         GccKnowsAbout (recordType) AND GccKnowsAbout (fieldType) AND
         CompletelyResolved (recordType) AND CompletelyResolved (fieldType)
      THEN
         IF GetMode(record)=LeftValue
         THEN
            ptr := BuildComponentRef (location,
                                      BuildIndirect (location, Mod2Gcc (record), Mod2Gcc (recordType)),
                                      Mod2Gcc (field))
         ELSE
            ptr := BuildComponentRef (location, Mod2Gcc (record), Mod2Gcc (field))
         END ;
         AddModGcc (result, ptr)
      ELSE
         InternalError ('symbol type should have been declared by now')
      END
   ELSE
      InternalError ('not expecting this type of symbol')
   END
END CodeRecordField ;


(*
   BuildHighFromChar -
*)

PROCEDURE BuildHighFromChar (operand: CARDINAL) : tree ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(GetDeclaredMod(operand)) ;
   IF IsConstString (operand) AND
      (IsConstStringM2nul (operand) OR IsConstStringCnul (operand))
   THEN
      RETURN GetCardinalOne (location)
   END ;
   RETURN GetCardinalZero (location)
END BuildHighFromChar ;


(*
   SkipToArray -
*)

PROCEDURE SkipToArray (operand, dim: CARDINAL) : CARDINAL ;
VAR
   type: CARDINAL ;
BEGIN
   WHILE dim>1 DO
      type := SkipType(GetType(operand)) ;
      IF IsArray(type)
      THEN
         operand := type
      END ;
      DEC(dim)
   END ;
   RETURN( operand )
END SkipToArray ;


(*
   BuildHighFromArray -
*)

PROCEDURE BuildHighFromArray (tokenno: CARDINAL; dim, operand: CARDINAL) : tree ;
VAR
   Type    : CARDINAL ;
   location: location_t ;
BEGIN
   location := TokenToLocation(tokenno) ;
   Type := SkipType (GetType (SkipToArray (operand, dim))) ;
   RETURN BuildHighFromStaticArray (location, (* dim, *) Type)
END BuildHighFromArray ;


(*
   BuildHighFromStaticArray -
*)

PROCEDURE BuildHighFromStaticArray (location: location_t; (* dim, *) Type: CARDINAL) : tree ;
VAR
   High, Low: CARDINAL ;
   Subscript,
   Subrange : CARDINAL ;
BEGIN
   Assert (IsArray (Type)) ;
   Subscript := GetArraySubscript (Type) ;
   Subrange := SkipType (GetType (Subscript)) ;
   IF IsEnumeration (Subrange)
   THEN
      GetBaseTypeMinMax (Subrange, Low, High) ;
      IF GccKnowsAbout (High)
      THEN
         RETURN tree (Mod2Gcc (High))
      END
   ELSIF IsSubrange(Subrange)
   THEN
      GetSubrange (Subrange, High, Low) ;
      IF GccKnowsAbout (Low) AND GccKnowsAbout (High)
      THEN
         RETURN BuildSub (location, Mod2Gcc (High), Mod2Gcc (Low), TRUE)
      END
   ELSE
      MetaError1 ('array subscript {%1EDad:for} must be a subrange or enumeration type', Type) ;
      RETURN tree(NIL)
   END ;
   IF GccKnowsAbout (High)
   THEN
      RETURN tree (Mod2Gcc (High))
   ELSE
      RETURN tree (NIL)
   END
END BuildHighFromStaticArray ;


(*
   BuildHighFromString -
*)

PROCEDURE BuildHighFromString (operand: CARDINAL) : tree ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (GetDeclaredMod (operand)) ;
   IF GccKnowsAbout (operand) AND (StringLength (Mod2Gcc (operand)) > 0)
   THEN
      RETURN( BuildIntegerConstant (StringLength (Mod2Gcc (operand))-1) )
   ELSE
      RETURN( GetIntegerZero (location) )
   END
END BuildHighFromString ;


(*
   ResolveHigh - given an Modula-2 operand, it resolves the HIGH(operand)
                 and returns a GCC constant symbol containing the value of
                 HIGH(operand).
*)

PROCEDURE ResolveHigh (tokenno: CARDINAL; dim, operand: CARDINAL) : tree ;
VAR
   Type    : CARDINAL ;
   location: location_t ;
BEGIN
   Type := SkipType(GetType(operand)) ;
   location := TokenToLocation(tokenno) ;

   IF (Type=Char) AND (dim=1)
   THEN
      RETURN( BuildHighFromChar(operand) )
   ELSIF IsConstString(operand) AND (dim=1)
   THEN
      RETURN( BuildHighFromString(operand) )
   ELSIF IsArray(Type)
   THEN
      RETURN( BuildHighFromArray(tokenno, dim, operand) )
   ELSIF IsUnbounded(Type)
   THEN
      RETURN( GetHighFromUnbounded(location, dim, operand) )
   ELSE
      MetaErrorT1 (tokenno,
                   'base procedure HIGH expects a variable of type array or a constant string or CHAR as its parameter, rather than {%1Etad}',
                   operand) ;
      RETURN( GetIntegerZero(location) )
   END
END ResolveHigh ;


(*
   FoldHigh - if the array is not dynamic then we should be able to
              remove the HighOp quadruple and assign op1 with
              the known compile time HIGH(op3).
*)

PROCEDURE FoldHigh (tokenno: CARDINAL; p: WalkAction;
                    quad: CARDINAL; op1, dim, op3: CARDINAL) ;
VAR
   t       : tree ;
   location: location_t ;
BEGIN
   (* firstly ensure that any constant literal is declared *)
   TryDeclareConstant(tokenno, op3) ;
   location := TokenToLocation(tokenno) ;
   IF GccKnowsAbout(op3) AND CompletelyResolved(op3)
   THEN
      t := ResolveHigh(tokenno, dim, op3) ;
      (* fine, we can take advantage of this and fold constants *)
      IF IsConst(op1) AND (t#tree(NIL))
      THEN
         PutConst(op1, Cardinal) ;
         AddModGcc(op1,
                   DeclareKnownConstant(location, GetCardinalType(),
                                        ToCardinal(location, t))) ;
         p(op1) ;
         NoChange := FALSE ;
         SubQuad(quad)
      ELSE
         (* we can still fold the expression, but not the assignment, however, we will
            not do this here but in CodeHigh
         *)
      END
   END
END FoldHigh ;


(*
   CodeHigh - encode a unary arithmetic operation.
*)

PROCEDURE CodeHigh (result, dim, array: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;

   (* firstly ensure that any constant literal is declared *)
   DeclareConstant (CurrentQuadToken, array) ;
   IF IsConst (result)
   THEN
      (* still have a constant which was not resolved, pass it to gcc *)
      ConstantKnownAndUsed (result,
                            DeclareKnownConstant(location,
                                                 GetM2ZType (),
                                                 ResolveHigh (CurrentQuadToken, dim, array)))
   ELSE
      BuildAssignmentStatement (location,
                                Mod2Gcc (result),
                                BuildConvert (location,
                                              Mod2Gcc (GetType (result)),
                                              ResolveHigh (CurrentQuadToken, dim, array),
                                              FALSE))
   END
END CodeHigh ;


(*
   CodeUnbounded - codes the creation of an unbounded parameter variable.
                   places the address of op3 into *op1
*)

PROCEDURE CodeUnbounded (result, array: CARDINAL) ;
VAR
   Addr    : tree ;
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;

   DeclareConstant (CurrentQuadToken, array) ;
   IF IsConstString (array) OR (IsConst (array) AND (GetSType (array) = Char))
   THEN
      BuildAssignmentStatement (location, Mod2Gcc (result), BuildAddr (location, PromoteToString (CurrentQuadToken, array), FALSE))
   ELSIF IsConstructor (array)
   THEN
      BuildAssignmentStatement (location, Mod2Gcc (result), BuildAddr (location, Mod2Gcc (array), TRUE))
   ELSIF IsUnbounded (GetType (array))
   THEN
      IF GetMode(array) = LeftValue
      THEN
         Addr := BuildConvert (location, Mod2Gcc (GetType (result)), Mod2Gcc (array), FALSE)
      ELSE
         Addr := BuildComponentRef (location, Mod2Gcc (array), Mod2Gcc (GetUnboundedAddressOffset (GetType (array))))
      END ;
      BuildAssignmentStatement (location, Mod2Gcc (result), Addr)
   ELSIF GetMode(array) = RightValue
   THEN
      BuildAssignmentStatement (location, Mod2Gcc (result), BuildAddr (location, Mod2Gcc (array), FALSE))
   ELSE
      BuildAssignmentStatement (location, Mod2Gcc (result), Mod2Gcc (array))
   END
END CodeUnbounded ;


(*
   AreSubrangesKnown - returns TRUE if the subranges values used within, array, are known.
*)

PROCEDURE AreSubrangesKnown (array: CARDINAL) : BOOLEAN ;
VAR
   type,
   subscript,
   low, high: CARDINAL ;
BEGIN
   IF GccKnowsAbout(array)
   THEN
      subscript := GetArraySubscript(array) ;
      IF subscript=NulSym
      THEN
         InternalError ('not expecting a NulSym as a subscript')
      ELSE
         type := SkipType(GetType(subscript)) ;
         low  := GetTypeMin(type) ;
         high := GetTypeMax(type) ;
         RETURN( GccKnowsAbout(low) AND GccKnowsAbout(high) )
      END
   ELSE
      RETURN( FALSE )
   END
END AreSubrangesKnown ;


(*
   CodeArray - res is an lvalue which will point to the array element.
*)

PROCEDURE CodeArray (res, index, array: CARDINAL) ;
VAR
   resType,
   arrayDecl,
   type,
   low,
   subscript  : CARDINAL ;
   a, ta,
   ti, tl     : tree ;
   location   : location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;

   arrayDecl := SkipType (GetType (array)) ;
   IF AreSubrangesKnown (arrayDecl)
   THEN
      subscript := GetArraySubscript (arrayDecl) ;
      type := SkipType (GetType (subscript)) ;
      low  := GetTypeMin (type) ;
      resType := GetVarBackEndType(res) ;
      IF resType=NulSym
      THEN
         resType := SkipType(GetType(res))
      END ;
      ta := Mod2Gcc(SkipType(GetType(arrayDecl))) ;
      IF GetMode(array)=LeftValue
      THEN
         a := BuildIndirect(location, Mod2Gcc(array), Mod2Gcc(SkipType(GetType(array))))
      ELSE
         a := Mod2Gcc(array)
      END ;
      IF IsArrayLarge(arrayDecl)
      THEN
         tl := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(low), FALSE) ;
         ti := BuildConvert(location, Mod2Gcc(type), Mod2Gcc(index), FALSE) ;
         ti := BuildConvert(location, GetIntegerType(), BuildSub(location, ti, tl, FALSE), FALSE) ;
         tl := GetIntegerZero(location)
      ELSE
         tl := BuildConvert(location, GetIntegerType(), Mod2Gcc(low), FALSE) ;
         ti := BuildConvert(location, GetIntegerType(), Mod2Gcc(index), FALSE)
      END ;
      (* ti := BuildConvert(location, GetIntegerType(), Mod2Gcc(high), FALSE) ; *)
      BuildAssignmentStatement (location,
                                Mod2Gcc (res),
                                BuildConvert (location,
                                              Mod2Gcc (resType),
                                              BuildAddr (location, BuildArray (location,
                                                                               ta, a, ti, tl),
                                                        FALSE),
                                              FALSE))
   ELSE
      InternalError ('subranges not yet resolved')
   END
END CodeArray ;


(*
   FoldElementSizeForArray - attempts to calculate the Subscript
                             multiplier for the index op3.
*)

PROCEDURE FoldElementSizeForArray (tokenno: CARDINAL; quad: CARDINAL;
                                   p: WalkAction;
                                   result, type: CARDINAL) ;
VAR
   Subscript: CARDINAL ;
   location : location_t ;
BEGIN
   location := TokenToLocation (tokenno) ;

   IF IsConst (result) AND (NOT GccKnowsAbout (result))
   THEN
      Subscript := GetArraySubscript (type) ;
      IF IsSizeSolved (Subscript)
      THEN
         PutConst (result, Integer) ;
         PushSize (Subscript) ;
         AddModGcc (result,
                    DeclareKnownConstant (location,
                                          GetCardinalType (),
                                          BuildConvert (location,
                                                        GetCardinalType (),
                                                        PopIntegerTree (),
                                                        TRUE))) ;
         p (result) ;
         NoChange := FALSE ;
         SubQuad (quad)
      END
   END
END FoldElementSizeForArray ;


(*
   FoldElementSizeForUnbounded - Unbounded arrays only have one index,
                                 therefore element size will be the
                                 TSIZE(Type) where Type is defined as:
                                 ARRAY OF Type.
*)

PROCEDURE FoldElementSizeForUnbounded (tokenno: CARDINAL; quad: CARDINAL;
                                       p: WalkAction;
                                       result, ArrayType: CARDINAL) ;
VAR
   Type    : CARDINAL ;
   location: location_t ;
BEGIN
   location := TokenToLocation (tokenno) ;

   IF IsConst (result)
   THEN
      IF GccKnowsAbout (result)
      THEN
         InternalError ('cannot assign a value twice to a constant')
      ELSE
         Assert (IsUnbounded (ArrayType)) ;
         Type := GetType (ArrayType) ;
         IF GccKnowsAbout (Type)
         THEN
            PutConst (result, Cardinal) ;
            AddModGcc (result,
                       DeclareKnownConstant (location,
                                             GetCardinalType (),
                                             BuildConvert (location,
                                                           GetCardinalType (),
                                                           FindSize (tokenno, Type),
                                                           TRUE))) ;
            p (result) ;
            NoChange := FALSE ;
            SubQuad (quad)
         END
      END
   END
END FoldElementSizeForUnbounded ;


(*
   FoldElementSize - folds the element size for an ArraySym or UnboundedSym.
                     ElementSize returns a constant which defines the
                     multiplier to be multiplied by this element index.
*)

PROCEDURE FoldElementSize (tokenno: CARDINAL; p: WalkAction;
                           quad: CARDINAL; result, type: CARDINAL) ;
BEGIN
   IF IsUnbounded (type)
   THEN
      FoldElementSizeForUnbounded (tokenno, quad, p, result, type)
   ELSIF IsArray (type)
   THEN
      FoldElementSizeForArray (tokenno, quad, p, result, type)
   ELSE
      InternalError ('expecting UnboundedSym or ArraySym')
   END
END FoldElementSize ;


(*
   PopKindTree - returns a Tree from M2ALU of the type implied by, op.
*)

PROCEDURE PopKindTree (op: CARDINAL; tokenno: CARDINAL) : tree ;
VAR
   type: CARDINAL ;
BEGIN
   IF IsConst (op) AND IsConstString (op)
   THEN
      (* Converting a nul char or char for example.  *)
      RETURN PopIntegerTree ()
   ELSE
      type := SkipType (GetType (op)) ;
      IF IsSet (type)
      THEN
         RETURN( PopSetTree (tokenno) )
      ELSIF IsRealType (type)
      THEN
         RETURN( PopRealTree () )
      ELSE
         RETURN( PopIntegerTree () )
      END
   END
END PopKindTree ;


(*
   FoldConvert - attempts to fold expr to type into result
                 providing that result and expr are constants.
                 If required convert will alter the machine representation
                 of expr to comply with type.
*)

PROCEDURE FoldConvert (tokenno: CARDINAL; p: WalkAction;
                       quad: CARDINAL; result, type, expr: CARDINAL) ;

VAR
   tl      : tree ;
   location: location_t ;
BEGIN
   location := TokenToLocation (tokenno) ;
   (* First ensure that constant literals are declared.  *)
   TryDeclareConstant (tokenno, expr) ;
   IF IsConstant (expr)
   THEN
      IF GccKnowsAbout (type) AND
         (IsProcedure (expr) OR IsValueSolved (expr)) AND
         GccKnowsAbout (SkipType (type))
      THEN
         (* The type is known and expr is resolved so fold the convert.  *)
         IF IsConst (result)
         THEN
            PutConst (result, type) ;   (* Change result type just in case.  *)
            tl := Mod2Gcc (SkipType (type)) ;
            IF IsProcedure (expr)
            THEN
               AddModGcc (result, BuildConvert (location, tl, Mod2Gcc (expr), TRUE))
            ELSE
               PushValue (expr) ;
               IF IsConstSet (expr)
               THEN
                  IF IsSet (SkipType (type))
                  THEN
                     WriteFormat0 ('cannot convert values between sets')
                  ELSE
                     PushIntegerTree (FoldAndStrip (BuildConvert (location, tl, PopSetTree (tokenno), TRUE))) ;
                     PopValue (result) ;
                     PushValue (result) ;
                     AddModGcc (result, PopIntegerTree())
                  END
               ELSE
                  IF IsSet (SkipType (type))
                  THEN
                     PushSetTree (tokenno,
                                  FoldAndStrip (BuildConvert (location, tl, PopKindTree (expr, tokenno),
                                                              TRUE)), SkipType (type)) ;
                     PopValue (result) ;
                     PutConstSet (result) ;
                     PushValue (result) ;
                     AddModGcc (result, PopSetTree (tokenno))
                  ELSIF IsRealType (SkipType (type))
                  THEN
                     PushRealTree (FoldAndStrip (BuildConvert (location, tl, PopKindTree (expr, tokenno),
                                                               TRUE))) ;
                     PopValue (result) ;
                     PushValue (result) ;
                     AddModGcc (result, PopKindTree (result, tokenno))
                  ELSE
                     (* Let CheckOverflow catch a potential overflow rather than BuildConvert.  *)
                     PushIntegerTree (FoldAndStrip (BuildConvert (location, tl,
                                                                  PopKindTree (expr, tokenno),
                                                                  FALSE))) ;
                     PopValue (result) ;
                     PushValue (result) ;
                     CheckOrResetOverflow (tokenno, PopKindTree (result, tokenno), MustCheckOverflow (quad)) ;
                     PushValue (result) ;
                     AddModGcc (result, PopKindTree (result, tokenno))
                  END
               END
            END ;
            p (result) ;
            NoChange := FALSE ;
            SubQuad (quad)
         END
      END
   END
END FoldConvert ;


(*
   CodeConvert - Converts, rhs, to, type, placing the result into lhs.
                 Convert will, if need be, alter the machine representation
                 of op3 to comply with TYPE op2.
*)

PROCEDURE CodeConvert (quad: CARDINAL; lhs, type, rhs: CARDINAL) ;
VAR
   tl, tr  : tree ;
   location: location_t ;
BEGIN
   CheckStop(quad) ;

   (* firstly ensure that constant literals are declared *)
   DeclareConstant(CurrentQuadToken, rhs) ;
   DeclareConstructor(CurrentQuadToken, quad, rhs) ;
   location := TokenToLocation(CurrentQuadToken) ;

   tl := LValueToGenericPtr(location, type) ;
   IF IsProcedure(rhs)
   THEN
      tr := BuildAddr(location, Mod2Gcc(rhs), FALSE)
   ELSE
      tr := LValueToGenericPtr(location, rhs) ;
      tr := ConvertRHS(tr, type, rhs)
   END ;
   IF IsConst(lhs)
   THEN
      (* fine, we can take advantage of this and fold constant *)
      PutConst(lhs, type) ;
      tl := Mod2Gcc(SkipType(type)) ;
      ConstantKnownAndUsed (lhs,
                            BuildConvert (location, tl, Mod2Gcc (rhs), TRUE))
   ELSE
      BuildAssignmentStatement (location, Mod2Gcc (lhs), BuildConvert (location, tl, tr, TRUE)) ;
   END
END CodeConvert ;


(*
   CodeCoerce - Coerce op3 to type op2 placing the result into
                op1.
                Coerce will NOT alter the machine representation
                of op3 to comply with TYPE op2.
                Therefore it _insists_ that under all circumstances that the
                type sizes of op1 and op3 are the same.
                CONVERT will perform machine manipulation to change variable
                types, coerce does no such thing.
*)

PROCEDURE CodeCoerce (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   DeclareConstant(CurrentQuadToken, op3) ;  (* checks to see whether it is a constant literal and declares it *)
   DeclareConstructor(CurrentQuadToken, quad, op3) ;
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsProcedure(op3)
   THEN
      IF AreConstantsEqual(FindSize(CurrentQuadToken, op1), FindSize(CurrentQuadToken, Address))
      THEN
         IF IsConst(op1)
         THEN
            ConstantKnownAndUsed(op1, CheckConstant(CurrentQuadToken, op1, op3))
         ELSE
            BuildAssignmentStatement (location, Mod2Gcc (op1), Mod2Gcc (op3))
         END
      ELSE
         MetaErrorT0 (CurrentQuadToken,
                      '{%E}procedure address can only be stored in an address sized operand')
      END
   ELSIF IsConst(op3) OR AreConstantsEqual(FindSize(CurrentQuadToken, op1), FindSize(CurrentQuadToken, op3))
   THEN
      IF IsConst(op1)
      THEN
         ConstantKnownAndUsed(op1,
                              DeclareKnownConstant(location,
                                                   Mod2Gcc(GetType(op1)),
                                                   Mod2Gcc(op3)))
      ELSE
         Assert(GccKnowsAbout(op2)) ;
         IF IsConst(op3)
         THEN
            BuildAssignmentStatement (location, Mod2Gcc(op1), Mod2Gcc(op3))
         ELSE
            (* does not work t := BuildCoerce(Mod2Gcc(op1), Mod2Gcc(op2), Mod2Gcc(op3)) *)
            checkDeclare (op1) ;
            AddStatement (location,
                          MaybeDebugBuiltinMemcpy(location,
                                                  BuildAddr(location, Mod2Gcc(op1), FALSE),
                                                  BuildAddr(location, Mod2Gcc(op3), FALSE),
                                                  FindSize(CurrentQuadToken, op2)))
         END
      END
   ELSE
      MetaErrorT0 (CurrentQuadToken,
                   'can only {%kCAST} objects of the same size')
   END
END CodeCoerce ;


(*
   FoldCoerce -
*)

PROCEDURE FoldCoerce (tokenno: CARDINAL; p: WalkAction;
                      quad, op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   TryDeclareConstant(tokenno, op3) ;  (* checks to see whether it is a constant literal and declares it *)
   location := TokenToLocation(tokenno) ;

   IF GccKnowsAbout(op2) AND GccKnowsAbout(op3)
   THEN
      IF IsProcedure(op3)
      THEN
         IF AreConstantsEqual(FindSize(tokenno, op1), FindSize(tokenno, Address))
         THEN
            IF IsConst(op1)
            THEN
               AddModGcc(op1,
                         DeclareKnownConstant(location,
                                              Mod2Gcc(GetType(op1)),
                                              Mod2Gcc(op3))) ;
               p(op1) ;
               NoChange := FALSE ;
               SubQuad(quad)
            END
         ELSE
            MetaErrorT0 (CurrentQuadToken,
                         '{%E}procedure address can only be stored in a address sized operand')
         END
      ELSIF IsConst(op3)
      THEN
         IF IsConst(op1)
         THEN
            AddModGcc(op1,
                      DeclareKnownConstant(location,
                                           Mod2Gcc(GetType(op1)),
                                           Mod2Gcc(op3))) ;
            p(op1) ;
            NoChange := FALSE ;
            SubQuad(quad)
         END
      END
   END
END FoldCoerce ;


(*
   CanConvert - returns TRUE if we can convert variable, var, to a, type.
*)

PROCEDURE CanConvert (type, var: CARDINAL) : BOOLEAN ;
VAR
   svar,
   stype: CARDINAL ;
BEGIN
   stype := SkipType(type) ;
   svar := SkipType(GetType(var)) ;
   RETURN (IsBaseType(stype) OR IsOrdinalType(stype) OR IsSystemType(stype)) AND
          (IsBaseType(svar) OR IsOrdinalType(svar) OR IsSystemType(stype))
END CanConvert ;


(*
   CodeCast - Cast op3 to type op2 placing the result into op1.
              Cast will NOT alter the machine representation
              of op3 to comply with TYPE op2 as long as SIZE(op3)=SIZE(op2).
              If the sizes differ then Convert is called.
*)

PROCEDURE CodeCast (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   DeclareConstant(CurrentQuadToken, op3) ;  (* checks to see whether it is a constant literal and declares it *)
   DeclareConstructor(CurrentQuadToken, quad, op3) ;
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsProcedure(op3)
   THEN
      IF AreConstantsEqual(FindSize(CurrentQuadToken, op1), FindSize(CurrentQuadToken, Address))
      THEN
         IF IsConst(op1)
         THEN
            ConstantKnownAndUsed(op1, CheckConstant(CurrentQuadToken, op1, op3))
         ELSE
            BuildAssignmentStatement (location, Mod2Gcc(op1), Mod2Gcc(op3))
         END
      ELSE
         MetaErrorT0 (CurrentQuadToken,
                      '{%E}procedure address can only be stored in an address sized operand')
      END
   ELSIF IsConst(op3) OR AreConstantsEqual(FindSize(CurrentQuadToken, op1), FindSize(CurrentQuadToken, op3))
   THEN
      CodeCoerce(quad, op1, op2, op3)
   ELSE
      IF PedanticCast AND (NOT CanConvert(op2, op3))
      THEN
         MetaError2 ('{%WkCAST} cannot copy a variable src {%2Dad} to a destination {%1Dad} as they are of different sizes and are not ordinal or real types',
                     op1, op3)
      END ;
      CodeConvert(quad, op1, op2, op3)
   END
END CodeCast ;


(*
   FoldCoerce -
*)

PROCEDURE FoldCast (tokenno: CARDINAL; p: WalkAction;
                    quad, op1, op2, op3: CARDINAL) ;
BEGIN
   TryDeclareConstant(tokenno, op3) ;  (* checks to see whether it is a constant literal and declares it *)
   IF GccKnowsAbout(op2) AND GccKnowsAbout(op3)
   THEN
      IF IsProcedure(op3)
      THEN
         IF AreConstantsEqual(FindSize(tokenno, op1), FindSize(tokenno, Address))
         THEN
            FoldCoerce(tokenno, p, quad, op1, op2, op3)
         ELSE
            MetaErrorT0 (tokenno,
                         '{%E}procedure address can only be stored in an address sized operand')
         END
      ELSIF IsConst(op3)
      THEN
         FoldCoerce(tokenno, p, quad, op1, op2, op3)
      END
   END
END FoldCast ;


(*
   CreateLabelProcedureN - creates a label using procedure name and
                           an integer.
*)

PROCEDURE CreateLabelProcedureN (proc: CARDINAL; leader: ARRAY OF CHAR;
                                 unboundedCount, n: CARDINAL) : String ;
VAR
   n1, n2: String ;
BEGIN
   n1 := Mark(InitStringCharStar(KeyToCharStar(GetSymName(proc)))) ;
   n2 := Mark(InitString(leader)) ;
   (* prefixed by .L unboundedCount and n to ensure that no Modula-2 identifiers clash *)
   RETURN( Sprintf4(Mark(InitString('.L%d.%d.unbounded.%s.%s')), unboundedCount, n, n1, n2) )
END CreateLabelProcedureN ;


(*
   CreateLabelName - creates a namekey from quadruple, q.
*)

PROCEDURE CreateLabelName (q: CARDINAL) : String ;
BEGIN
   (* prefixed by . to ensure that no Modula-2 identifiers clash *)
   RETURN( Sprintf1(Mark(InitString('.L%d')), q) )
END CreateLabelName ;


(*
   CodeGoto - creates a jump to a labeled quadruple.
*)

PROCEDURE CodeGoto (destquad: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;
   BuildGoto (location, string (CreateLabelName (destquad)))
END CodeGoto ;


(*
   CheckReferenced - checks to see whether this quadruple requires a label.
*)

PROCEDURE CheckReferenced (quad: CARDINAL; op: QuadOperator) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   (* we do not create labels for procedure entries *)
   IF (op#ProcedureScopeOp) AND (op#NewLocalVarOp) AND IsReferenced(quad)
   THEN
      DeclareLabel(location, string(CreateLabelName(quad)))
   END
END CheckReferenced ;


(*
   CodeIfSetLess -
*)

PROCEDURE CodeIfSetLess (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   settype   : CARDINAL ;
   falselabel: ADDRESS ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1) AND IsConst(op2)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst(op1)
   THEN
      settype := SkipType(GetType(op2))
   ELSE
      settype := SkipType(GetType(op1))
   END ;
   IF CompareTrees(FindSize(CurrentQuadToken, settype), FindSize(CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump(location,
             BuildIsNotSuperset(location,
                                BuildConvert(location, GetWordType(), Mod2Gcc(op1), FALSE),
                                BuildConvert(location, GetWordType(), Mod2Gcc(op2), FALSE)),
             NIL, string(CreateLabelName(op3)))
   ELSE
      falselabel := string(Sprintf1(Mark(InitString('.Lset%dcomp')), quad)) ;

      BuildForeachWordInSetDoIfExpr(location,
                                    Mod2Gcc(settype),
                                    Mod2Gcc(op1), Mod2Gcc(op2),
                                    GetMode(op1)=LeftValue,
                                    GetMode(op2)=LeftValue,
                                    IsConst(op1), IsConst(op2),
                                    BuildIsSuperset,
                                    falselabel) ;

      BuildGoto(location, string(CreateLabelName(op3))) ;
      DeclareLabel(location, falselabel)
   END
END CodeIfSetLess ;


(*
   PerformCodeIfLess - codes the quadruple if op1 < op2 then goto op3
*)

PROCEDURE PerformCodeIfLess (quad: CARDINAL) ;
VAR
   tl, tr  : tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   GetQuadOtok (quad, combined, op,
                left, right, dest, overflow,
                constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;

   IF IsConst(left) AND IsConst(right)
   THEN
      PushValue(left) ;
      PushValue(right) ;
      IF Less(CurrentQuadToken)
      THEN
         BuildGoto(location, string(CreateLabelName(dest)))
      ELSE
         (* Fall through.  *)
      END
   ELSIF IsConstSet(left) OR (IsVar(left) AND IsSet(SkipType(GetType(left)))) OR
         IsConstSet(right) OR (IsVar(right) AND IsSet(SkipType(GetType(right))))
   THEN
      CodeIfSetLess(quad, left, right, dest)
   ELSE
      IF IsComposite(GetType(left)) OR IsComposite(GetType(right))
      THEN
         MetaErrorT2 (combined,
                      'comparison tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands (location,
                                tl, tr,
                                ComparisonMixTypes (left, right,
                                                    SkipType (GetType (left)),
                                                    SkipType (GetType (right)),
                                                    combined),
                                left, right) ;
         DoJump (location,
                 BuildLessThan (location, tl, tr), NIL, string (CreateLabelName (dest)))
      END
   END
END PerformCodeIfLess ;


(*
   CodeIfLess - codes the quadruple if op1 < op2 then goto op3
*)

PROCEDURE CodeIfLess (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfLess (quad)
   END
END CodeIfLess ;


(*
   CodeIfSetGre -
*)

PROCEDURE CodeIfSetGre (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   settype   : CARDINAL ;
   falselabel: ADDRESS ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1) AND IsConst(op2)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst(op1)
   THEN
      settype := SkipType(GetType(op2))
   ELSE
      settype := SkipType(GetType(op1))
   END ;
   IF CompareTrees(FindSize(CurrentQuadToken, settype), FindSize(CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump(location,
             BuildIsNotSubset(location,
                              BuildConvert(location, GetWordType(), Mod2Gcc(op1), FALSE),
                              BuildConvert(location, GetWordType(), Mod2Gcc(op2), FALSE)),
             NIL, string(CreateLabelName(op3)))
   ELSE
      falselabel := string(Sprintf1(Mark(InitString('.Lset%dcomp')), quad)) ;

      BuildForeachWordInSetDoIfExpr(location,
                                    Mod2Gcc(settype),
                                    Mod2Gcc(op1), Mod2Gcc(op2),
                                    GetMode(op1)=LeftValue,
                                    GetMode(op2)=LeftValue,
                                    IsConst(op1), IsConst(op2),
                                    BuildIsSubset,
                                    falselabel) ;

      BuildGoto(location, string(CreateLabelName(op3))) ;
      DeclareLabel(location, falselabel)
   END
END CodeIfSetGre ;


(*
   PerformCodeIfGre - codes the quadruple if op1 > op2 then goto op3
*)

PROCEDURE PerformCodeIfGre (quad: CARDINAL) ;
VAR
   tl, tr  : tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   GetQuadOtok (quad, combined, op,
                left, right, dest, overflow, constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst(left) AND IsConst(right)
   THEN
      PushValue(left) ;
      PushValue(right) ;
      IF Gre(combined)
      THEN
         BuildGoto(location, string(CreateLabelName(dest)))
      ELSE
         (* fall through *)
      END
   ELSIF IsConstSet(left) OR (IsVar(left) AND IsSet(SkipType(GetType(left)))) OR
         IsConstSet(right) OR (IsVar(right) AND IsSet(SkipType(GetType(right))))
   THEN
      CodeIfSetGre(quad, left, right, dest)
   ELSE
      IF IsComposite(GetType(left)) OR IsComposite(GetType(right))
      THEN
         MetaErrorT2 (combined,
                      'comparison tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands(location,
                               tl, tr,
                               ComparisonMixTypes (left, right,
                                                   SkipType (GetType (left)),
                                                   SkipType (GetType (right)),
                                                   combined),
                               left, right) ;
         DoJump(location, BuildGreaterThan(location, tl, tr), NIL, string(CreateLabelName(dest)))
      END
   END
END PerformCodeIfGre ;


(*
   CodeIfGre - codes the quadruple if op1 > op2 then goto op3
*)

PROCEDURE CodeIfGre (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfGre (quad)
   END
END CodeIfGre ;


(*
   CodeIfSetLessEqu -
*)

PROCEDURE CodeIfSetLessEqu (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   settype   : CARDINAL ;
   falselabel: ADDRESS ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1) AND IsConst(op2)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst(op1)
   THEN
      settype := SkipType(GetType(op2))
   ELSE
      settype := SkipType(GetType(op1))
   END ;
   IF CompareTrees(FindSize(CurrentQuadToken, settype), FindSize(CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump(location,
             BuildIsSubset(location,
                           BuildConvert(location, GetWordType(), Mod2Gcc(op1), FALSE),
                           BuildConvert(location, GetWordType(), Mod2Gcc(op2), FALSE)),
             NIL, string(CreateLabelName(op3)))
   ELSE
      falselabel := string(Sprintf1(Mark(InitString('.Lset%dcomp')), quad)) ;

      BuildForeachWordInSetDoIfExpr(location,
                                    Mod2Gcc(settype),
                                    Mod2Gcc(op1), Mod2Gcc(op2),
                                    GetMode(op1)=LeftValue,
                                    GetMode(op2)=LeftValue,
                                    IsConst(op1), IsConst(op2),
                                    BuildIsNotSubset,
                                    falselabel) ;

      BuildGoto(location, string(CreateLabelName(op3))) ;
      DeclareLabel(location, falselabel)
   END
END CodeIfSetLessEqu ;


(*
   PerformCodeIfLessEqu - codes the quadruple if op1 <= op2 then goto op3
*)

PROCEDURE PerformCodeIfLessEqu (quad: CARDINAL) ;
VAR
   tl, tr  : tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                overflow, constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst(left) AND IsConst(right)
   THEN
      PushValue(left) ;
      PushValue(right) ;
      IF LessEqu(combined)
      THEN
         BuildGoto(location, string(CreateLabelName(dest)))
      ELSE
         (* fall through *)
      END
   ELSIF IsConstSet (left) OR (IsVar (left) AND IsSet (SkipType (GetType (left)))) OR
         IsConstSet (right) OR (IsVar (right) AND IsSet (SkipType (GetType (right))))
   THEN
      CodeIfSetLessEqu (quad, left, right, dest)
   ELSE
      IF IsComposite (GetType (left)) OR IsComposite (GetType (right))
      THEN
         MetaErrorT2 (combined,
                      'comparison tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands (location,
                                tl, tr,
                                ComparisonMixTypes (left, right,
                                                    SkipType (GetType (left)),
                                                    SkipType (GetType (right)),
                                                    combined),
                                left, right) ;
         DoJump (location, BuildLessThanOrEqual (location, tl, tr),
                 NIL, string (CreateLabelName (dest)))
      END
   END
END PerformCodeIfLessEqu ;


(*
   CodeIfLessEqu - codes the quadruple if op1 <= op2 then goto op3
*)

PROCEDURE CodeIfLessEqu (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfLessEqu (quad)
   END
END CodeIfLessEqu ;


(*
   CodeIfSetGreEqu -
*)

PROCEDURE CodeIfSetGreEqu (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   settype   : CARDINAL ;
   falselabel: ADDRESS ;
   location: location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1) AND IsConst(op2)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst(op1)
   THEN
      settype := SkipType(GetType(op2))
   ELSE
      settype := SkipType(GetType(op1))
   END ;
   IF CompareTrees(FindSize(CurrentQuadToken, settype), FindSize(CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump(location,
             BuildIsSuperset(location,
                             BuildConvert(location, GetWordType(), Mod2Gcc(op1), FALSE),
                             BuildConvert(location, GetWordType(), Mod2Gcc(op2), FALSE)),
             NIL, string(CreateLabelName(op3)))
   ELSE
      falselabel := string(Sprintf1(Mark(InitString('.Lset%dcomp')), quad)) ;

      BuildForeachWordInSetDoIfExpr(location,
                                    Mod2Gcc(settype),
                                    Mod2Gcc(op1), Mod2Gcc(op2),
                                    GetMode(op1)=LeftValue,
                                    GetMode(op2)=LeftValue,
                                    IsConst(op1), IsConst(op2),
                                    BuildIsNotSuperset,
                                    falselabel) ;

      BuildGoto(location, string(CreateLabelName(op3))) ;
      DeclareLabel(location, falselabel)
   END
END CodeIfSetGreEqu ;


(*
   PerformCodeIfGreEqu - codes the quadruple if op1 >= op2 then goto op3
*)

PROCEDURE PerformCodeIfGreEqu (quad: CARDINAL) ;
VAR
   tl, tr: tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                overflow, constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst(left) AND IsConst(right)
   THEN
      PushValue(left) ;
      PushValue(right) ;
      IF GreEqu(combined)
      THEN
         BuildGoto(location, string(CreateLabelName(dest)))
      ELSE
         (* fall through *)
      END
   ELSIF IsConstSet(left) OR (IsVar(left) AND IsSet(SkipType(GetType(left)))) OR
         IsConstSet(right) OR (IsVar(right) AND IsSet(SkipType(GetType(right))))
   THEN
      CodeIfSetGreEqu(quad, left, right, dest)
   ELSE
      IF IsComposite(GetType(left)) OR IsComposite(GetType(right))
      THEN
         MetaErrorT2 (combined,
                      'comparison tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands(location,
                               tl, tr,
                               ComparisonMixTypes (left, right,
                                                   SkipType (GetType (left)),
                                                   SkipType (GetType (right)),
                                                   combined),
                               left, right) ;
         DoJump(location, BuildGreaterThanOrEqual(location, tl, tr), NIL, string(CreateLabelName(dest)))
      END
   END
END PerformCodeIfGreEqu ;


(*
   CodeIfGreEqu - codes the quadruple if op1 >= op2 then goto op3
*)

PROCEDURE CodeIfGreEqu (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfGreEqu (quad)
   END
END CodeIfGreEqu ;


(*
   CodeIfSetEqu - codes if op1 = op2 then goto op3
                  Note that if op1 and op2 are not both constants
                  since this will have been evaluated in CodeIfEqu.
*)

PROCEDURE CodeIfSetEqu (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   settype   : CARDINAL ;
   falselabel: ADDRESS ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst(op1) AND IsConst(op2)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst(op1)
   THEN
      settype := SkipType(GetType(op2))
   ELSE
      settype := SkipType(GetType(op1))
   END ;
   IF CompareTrees(FindSize(CurrentQuadToken, settype), FindSize(CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump(location,
             BuildEqualTo(location,
                          BuildConvert(location, GetWordType(), Mod2Gcc(op1), FALSE),
                          BuildConvert(location, GetWordType(), Mod2Gcc(op2), FALSE)),
             NIL, string(CreateLabelName(op3)))
   ELSIF GetSType(op1)=GetSType(op2)
   THEN
      falselabel := string(Sprintf1(Mark(InitString('.Lset%dcomp')), quad)) ;

      BuildForeachWordInSetDoIfExpr(location,
                                    Mod2Gcc(settype),
                                    Mod2Gcc(op1), Mod2Gcc(op2),
                                    GetMode(op1)=LeftValue,
                                    GetMode(op2)=LeftValue,
                                    IsConst(op1), IsConst(op2),
                                    BuildNotEqualTo,
                                    falselabel) ;

      BuildGoto(location, string(CreateLabelName(op3))) ;
      DeclareLabel(location, falselabel)
   ELSE
      MetaErrorT2 (CurrentQuadToken,
                   'set comparison is only allowed between the same set type, the set types used by {%1Eatd} and {%2atd} are different',
                   op1, op2)
   END
END CodeIfSetEqu ;


(*
   CodeIfSetNotEqu - codes if op1 # op2 then goto op3
                     Note that if op1 and op2 are not both constants
                     since this will have been evaluated in CodeIfNotEqu.
*)

PROCEDURE CodeIfSetNotEqu (left, right, destQuad: CARDINAL) ;
VAR
   settype  : CARDINAL ;
   truelabel: ADDRESS ;
   location  : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   IF IsConst (left) AND IsConst (right)
   THEN
      InternalError ('this should have been folded in the calling procedure')
   ELSIF IsConst (left)
   THEN
      settype := SkipType (GetType (right))
   ELSE
      settype := SkipType (GetType (left))
   END ;
   IF CompareTrees (FindSize (CurrentQuadToken, settype), FindSize (CurrentQuadToken, Word)) <= 0
   THEN
      (* word size sets *)
      DoJump (location,
              BuildNotEqualTo(location,
                              BuildConvert (location, GetWordType (), Mod2Gcc (left), FALSE),
                              BuildConvert (location, GetWordType (), Mod2Gcc (right), FALSE)),
              NIL, string (CreateLabelName (destQuad)))
   ELSIF GetSType (left) = GetSType (right)
   THEN
      truelabel := string (CreateLabelName (destQuad)) ;

      BuildForeachWordInSetDoIfExpr (location,
                                     Mod2Gcc (settype),
                                     Mod2Gcc (left), Mod2Gcc (right),
                                     GetMode (left) = LeftValue,
                                     GetMode (right) = LeftValue,
                                     IsConst (left), IsConst (right),
                                     BuildNotEqualTo,
                                     truelabel)
   ELSE
      MetaErrorT2 (CurrentQuadToken,
                   'set comparison is only allowed between the same set type, the set types used by {%1Eatd} and {%2atd} are different',
                   left, right)
   END
END CodeIfSetNotEqu ;


(*
   ComparisonMixTypes -
*)

PROCEDURE ComparisonMixTypes (varleft, varright, left, right: CARDINAL; tokpos: CARDINAL) : CARDINAL ;
BEGIN
   IF IsGenericSystemType (left)
   THEN
      RETURN left
   ELSIF IsGenericSystemType (right)
   THEN
      RETURN right
   ELSE
      RETURN MixTypesDecl (varleft, varright, left, right, tokpos)
   END
END ComparisonMixTypes ;


(*
   PerformCodeIfEqu -
*)

PROCEDURE PerformCodeIfEqu (quad: CARDINAL) ;
VAR
   tl, tr                     : tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                overflow, constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      PushValue (left) ;
      PushValue (right) ;
      IF Equ (combined)
      THEN
         BuildGoto (location, string (CreateLabelName (dest)))
      ELSE
         (* Fall through.  *)
      END
   ELSIF IsConstSet (left) OR (IsVar (left) AND IsSet (SkipType (GetType (left)))) OR
         IsConstSet (right) OR (IsVar (right) AND IsSet (SkipType (GetType (right))))
   THEN
      CodeIfSetEqu (quad, left, right, dest)
   ELSE
      IF IsComposite (GetType (left)) OR IsComposite (GetType (right))
      THEN
         MetaErrorT2 (combined,
                      'equality tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands (location,
                                tl, tr,
                                ComparisonMixTypes (left, right,
                                                    SkipType (GetType (left)),
                                                    SkipType (GetType (right)),
                                                    combined),
                               left, right) ;
         DoJump (location, BuildEqualTo (location, tl, tr), NIL,
                 string (CreateLabelName (dest)))
      END
   END
END PerformCodeIfEqu ;


(*
   PerformCodeIfNotEqu -
*)

PROCEDURE PerformCodeIfNotEqu (quad: CARDINAL) ;
VAR
   tl, tr                     : tree ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   (* Ensure that any remaining undeclared constant literal is declared.  *)
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                constExpr, overflow,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst (left) AND IsConst (right)
   THEN
      PushValue (left) ;
      PushValue (right) ;
      IF NotEqu (combined)
      THEN
         BuildGoto (location, string (CreateLabelName (dest)))
      ELSE
         (* Fall through.  *)
      END
   ELSIF IsConstSet (left) OR (IsVar (left) AND IsSet (SkipType (GetType (left)))) OR
         IsConstSet (right) OR (IsVar (right) AND IsSet (SkipType (GetType (right))))
   THEN
      CodeIfSetNotEqu (left, right, dest)
   ELSE
      IF IsComposite (GetType (left)) OR IsComposite (GetType (right))
      THEN
         MetaErrorT2 (combined,
                      'inequality tests between composite types not allowed {%1Eatd} and {%2atd}',
                      left, right)
      ELSE
         ConvertBinaryOperands (location,
                                tl, tr,
                                ComparisonMixTypes (left, right,
                                                    SkipType (GetType (left)),
                                                    SkipType (GetType (right)),
                                                    combined),
                                left, right) ;
         DoJump (location, BuildNotEqualTo (location, tl, tr), NIL,
                 string (CreateLabelName (dest)))
      END
   END
END PerformCodeIfNotEqu ;


(*
   IsValidExpressionRelOp - declare left and right constants (if they are not already declared).
                            Check whether left and right are expression compatible.
*)

PROCEDURE IsValidExpressionRelOp (quad: CARDINAL; isin: BOOLEAN) : BOOLEAN ;
CONST
   Verbose = FALSE ;
VAR
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   (* Ensure that any remaining undeclared constant literal is declared.  *)
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                constExpr, overflow,
                leftpos, rightpos, destpos) ;
   DeclareConstant (leftpos, left) ;
   DeclareConstant (rightpos, right) ;
   DeclareConstructor (leftpos, quad, left) ;
   DeclareConstructor (rightpos, quad, right) ;
   IF ExpressionTypeCompatible (combined, "", left, right,
                                StrictTypeChecking, isin)
   THEN
      RETURN TRUE
   ELSE
      IF Verbose
      THEN
         MetaErrorT2 (combined,
                      'expression mismatch between {%1Etad} and {%2tad} seen during comparison',
                      left, right)
      END ;
      RETURN FALSE
   END
END IsValidExpressionRelOp ;


(*
   CodeIfEqu - codes the quadruple if op1 = op2 then goto op3
*)

PROCEDURE CodeIfEqu (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfEqu (quad)
   END
END CodeIfEqu ;


(*
   CodeIfNotEqu - codes the quadruple if op1 # op2 then goto op3
*)

PROCEDURE CodeIfNotEqu (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, FALSE)
   THEN
      PerformCodeIfNotEqu (quad)
   END
END CodeIfNotEqu ;


(*
   MixTypes3 - returns a type compatible from, low, high, var.
*)

PROCEDURE MixTypes3 (low, high, var: CARDINAL; tokenno: CARDINAL) : CARDINAL ;
VAR
   type: CARDINAL ;
BEGIN
   type := MixTypes(SkipType(GetType(low)), SkipType(GetType(high)), tokenno) ;
   type := MixTypes(type, SkipType(GetType(var)), tokenno) ;
   RETURN( type )
END MixTypes3 ;


(*
   BuildIfVarInConstValue - if var in constsetvalue then goto trueexit
*)

PROCEDURE BuildIfVarInConstValue (location: location_t; tokenno: CARDINAL;
                                  constsetvalue: PtrToValue; var, trueexit: CARDINAL) ;
VAR
   vt, lt, ht  : tree ;
   type,
   low, high, n: CARDINAL ;
   truelabel   : String ;
BEGIN
   n := 1 ;
   truelabel := string(CreateLabelName(trueexit)) ;
   WHILE GetRange(constsetvalue, n, low, high) DO
      type := MixTypes3(low, high, var, tokenno) ;
      ConvertBinaryOperands(location, vt, lt, type, var, low) ;
      ConvertBinaryOperands(location, ht, lt, type, high, low) ;
      BuildIfInRangeGoto(location, vt, lt, ht, truelabel) ;
      INC(n)
   END
END BuildIfVarInConstValue ;


(*
   BuildIfNotVarInConstValue - if not (var in constsetvalue) then goto trueexit
*)

PROCEDURE BuildIfNotVarInConstValue (quad: CARDINAL; constsetvalue: PtrToValue; var, trueexit: CARDINAL) ;
VAR
   vt, lt, ht  : tree ;
   type,
   low, high, n: CARDINAL ;
   falselabel,
   truelabel   : String ;
   location    : location_t ;
BEGIN
   location := TokenToLocation(CurrentQuadToken) ;

   truelabel := string(CreateLabelName(trueexit)) ;
   n := 1 ;
   WHILE GetRange(constsetvalue, n, low, high) DO
      INC(n)
   END ;
   IF n=2
   THEN
      (* actually only one set range, so we invert it *)
      type := MixTypes3(low, high, var, CurrentQuadToken) ;
      ConvertBinaryOperands(location, vt, lt, type, var, low) ;
      ConvertBinaryOperands(location, ht, lt, type, high, low) ;
      BuildIfNotInRangeGoto(location, vt, lt, ht, truelabel)
   ELSE
      n := 1 ;
      falselabel := string(Sprintf1(Mark(InitString('.Lset%d')), quad)) ;
      WHILE GetRange(constsetvalue, n, low, high) DO
         type := MixTypes3(low, high, var, CurrentQuadToken) ;
         ConvertBinaryOperands(location, vt, lt, type, var, low) ;
         ConvertBinaryOperands(location, ht, lt, type, high, low) ;
         BuildIfInRangeGoto(location, vt, lt, ht, falselabel) ;
         INC(n)
      END ;
      BuildGoto(location, truelabel) ;
      DeclareLabel(location, falselabel)
   END
END BuildIfNotVarInConstValue ;


(*
   PerformCodeIfIn - code the quadruple: if op1 in op2 then goto op3
*)

PROCEDURE PerformCodeIfIn (quad: CARDINAL) ;
VAR
   low,
   high    : CARDINAL ;
   lowtree,
   hightree,
   offset  : tree ;
   fieldno : INTEGER ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   (* Ensure that any remaining undeclared constant literal is declared.  *)
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                constExpr, overflow,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst(left) AND IsConst(right)
   THEN
      InternalError ('should not get to here (if we do we should consider calling FoldIfIn)')
   ELSIF CheckElementSetTypes (quad)
   THEN
      IF IsConst(left)
      THEN
         fieldno := GetFieldNo(combined, left, GetType(right), offset) ;
         IF fieldno>=0
         THEN
            PushValue(left) ;
            PushIntegerTree(offset) ;
            ConvertToType(GetType(left)) ;
            Sub ;
            BuildIfConstInVar(location,
                              Mod2Gcc(SkipType(GetType(right))),
                              Mod2Gcc(right), PopIntegerTree(),
                              GetMode(right)=LeftValue, fieldno,
                              string(CreateLabelName(dest)))
         ELSE
            MetaErrorT1 (combined, 'bit exceeded the range of set {%1Eatd}', left)
         END
      ELSIF IsConst(right)
      THEN
         (* builds a cascaded list of if statements *)
         PushValue(right) ;
         BuildIfVarInConstValue(location, combined, GetValue(combined), left, dest)
      ELSE
         GetSetLimits(SkipType(GetType(right)), low, high) ;

         PushValue(low) ;
         lowtree := PopIntegerTree() ;
         PushValue(high) ;
         hightree := PopIntegerTree() ;

         BuildIfVarInVar(location,
                         Mod2Gcc(SkipType(GetType(right))),
                         Mod2Gcc(right), Mod2Gcc(left),
                         GetMode(right)=LeftValue,
                         lowtree, hightree,
                         string(CreateLabelName(dest)))
      END
   END
END PerformCodeIfIn ;


(*
   PerformCodeIfNotIn - code the quadruple: if not (op1 in op2) then goto op3
*)

PROCEDURE PerformCodeIfNotIn (quad: CARDINAL) ;
VAR
   low,
   high    : CARDINAL ;
   lowtree,
   hightree,
   offset  : tree ;
   fieldno : INTEGER ;
   location                   : location_t ;
   left, right, dest, combined,
   leftpos, rightpos, destpos : CARDINAL ;
   constExpr, overflow        : BOOLEAN ;
   op                         : QuadOperator ;
BEGIN
   (* Ensure that any remaining undeclared constant literal is declared.  *)
   GetQuadOtok (quad, combined, op,
                left, right, dest,
                overflow, constExpr,
                leftpos, rightpos, destpos) ;
   location := TokenToLocation (combined) ;
   IF IsConst(left) AND IsConst(right)
   THEN
      InternalError ('should not get to here (if we do we should consider calling FoldIfIn)')
   ELSIF CheckElementSetTypes (quad)
   THEN
      IF IsConst(left)
      THEN
         fieldno := GetFieldNo(combined, left, SkipType(GetType(right)), offset) ;
         IF fieldno>=0
         THEN
            PushValue(left) ;
            PushIntegerTree(offset) ;
            ConvertToType(GetType(left)) ;
            Sub ;
            BuildIfNotConstInVar(location,
                                 Mod2Gcc(SkipType(GetType(right))),
                                 Mod2Gcc(right), PopIntegerTree(),
                                 GetMode(right)=LeftValue, fieldno,
                                 string(CreateLabelName(dest)))
         ELSE
            MetaErrorT1 (combined, 'bit exceeded the range of set {%1Eatd}', right)
         END
      ELSIF IsConst(right)
      THEN
         (* builds a cascaded list of if statements *)
         PushValue(right) ;
         BuildIfNotVarInConstValue(quad, GetValue(combined), left, dest)
      ELSE
         GetSetLimits(SkipType(GetType(right)), low, high) ;

         PushValue(low) ;
         lowtree := PopIntegerTree() ;
         PushValue(high) ;
         hightree := PopIntegerTree() ;

         BuildIfNotVarInVar(location,
                            Mod2Gcc(SkipType(GetType(right))),
                            Mod2Gcc(right), Mod2Gcc(left),
                            GetMode(right)=LeftValue,
                            lowtree, hightree,
                            string(CreateLabelName(dest)))
      END
   END
END PerformCodeIfNotIn ;


(*
   CodeIfIn - code the quadruple: if op1 in op2 then goto op3
*)

PROCEDURE CodeIfIn (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, TRUE)
   THEN
      PerformCodeIfIn (quad)
   END
END CodeIfIn ;


(*
   CodeIfNotIn - code the quadruple: if not (op1 in op2) then goto op3
*)

PROCEDURE CodeIfNotIn (quad: CARDINAL) ;
BEGIN
   IF IsValidExpressionRelOp (quad, TRUE)
   THEN
      PerformCodeIfNotIn (quad)
   END
END CodeIfNotIn ;


(*
------------------------------------------------------------------------------
   IndrX Operator           a = *b
------------------------------------------------------------------------------
   Sym1<X>   IndrX   Sym2<I>     Meaning     Mem[Sym1<I>] := Mem[constant]
   Sym1<X>   IndrX   Sym2<X>     Meaning     Mem[Sym1<I>] := Mem[Mem[Sym3<I>]]

   (op2 is the type of the data being indirectly copied)
*)

PROCEDURE CodeIndrX (quad: CARDINAL; op1, op2, op3: CARDINAL) ;
VAR
   location: location_t ;
BEGIN
   location := TokenToLocation (CurrentQuadToken) ;

   (*
      Follow the Quadruple rules:
   *)
   DeclareConstant (CurrentQuadToken, op3) ;  (* checks to see whether it is a constant and declares it *)
   DeclareConstructor (CurrentQuadToken, quad, op3) ;
   IF IsConstString (op3)
   THEN
      InternalError ('not expecting to index through a constant string')
   ELSE
      (*
         Mem[op1] := Mem[Mem[op3]]
      *)
      BuildAssignmentStatement (location, Mod2Gcc (op1), BuildIndirect (location, Mod2Gcc (op3), Mod2Gcc (op2)))
   END
END CodeIndrX ;


(*
   CodeXIndr - operands for XIndrOp are: left type right.
                *left = right.  The second operand is the type of the data being
                indirectly copied.
*)

PROCEDURE CodeXIndr (quad: CARDINAL) ;
VAR
   constExpr,
   overflowChecking: BOOLEAN ;
   op              : QuadOperator ;
   tokenno,
   left,
   type,
   right,
   leftpos,
   rightpos,
   typepos,
   xindrpos        : CARDINAL ;
   length,
   newstr          : tree ;
   location        : location_t ;
BEGIN
   GetQuadOtok (quad, xindrpos, op, left, type, right,
                overflowChecking, constExpr,
                leftpos, typepos, rightpos) ;
   tokenno := MakeVirtualTok (xindrpos, leftpos, rightpos) ;
   location := TokenToLocation (tokenno) ;

   type := SkipType (type) ;
   DeclareConstant (rightpos, right) ;
   DeclareConstructor (rightpos, quad, right) ;
   IF IsProcType(SkipType(type))
   THEN
      BuildAssignmentStatement (location, BuildIndirect (location, Mod2Gcc (left), GetPointerType ()), Mod2Gcc (right))
   ELSIF IsConstString (right) AND (GetStringLength (rightpos, right) = 0) AND (GetMode (left) = LeftValue)
   THEN
      (*
         no need to check for type errors,
         but we handle nul string as a special case as back end
         complains if we pass through a "" and ask it to copy the
         contents.
      *)
      BuildAssignmentStatement (location,
                                BuildIndirect (location, LValueToGenericPtr (location, left), Mod2Gcc (Char)),
                                StringToChar (Mod2Gcc (right), Char, right))
   ELSIF IsConstString (right) AND (SkipTypeAndSubrange (GetType (left)) # Char)
   THEN
      IF NOT PrepareCopyString (tokenno, length, newstr, right, type)
      THEN
         MetaErrorT2 (MakeVirtualTok (xindrpos, leftpos, rightpos),
                      'string constant {%1Ea} is too large to be assigned to the array {%2ad}',
                      right, left)
      END ;
      AddStatement (location,
                    MaybeDebugBuiltinMemcpy (location,
                                             Mod2Gcc (left),
                                             BuildAddr (location, newstr, FALSE),
                                             length))
   ELSE
      BuildAssignmentStatement (location,
                                BuildIndirect (location, Mod2Gcc (left), Mod2Gcc (type)),
                                ConvertRHS (Mod2Gcc (right), type, right))
   END
END CodeXIndr ;


(*
   InitBuiltinSyms -
*)

PROCEDURE InitBuiltinSyms (tok: CARDINAL) ;
BEGIN
   IF Memset = NulSym
   THEN
      Memset := FromModuleGetSym (tok, MakeKey ('memset'), MakeDefinitionSource (tok, MakeKey ('Builtins')))
   END ;
   IF Memcpy = NulSym
   THEN
      Memcpy := FromModuleGetSym (tok, MakeKey ('memcpy'), MakeDefinitionSource (tok, MakeKey ('Builtins')))
   END ;
END InitBuiltinSyms ;


BEGIN
   Memset := NulSym ;
   Memcpy := NulSym ;
   UnboundedLabelNo := 0 ;
   CurrentQuadToken := 0 ;
   ScopeStack := InitStackWord ()
END M2GenGCC.