| (* 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) | |
| 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) ; |
| BEGIN |
| IF DeclaredOperandsBecomes (p, quad) |
| THEN |
| IF (NOT IsConditionalBooleanQuad (quad)) OR IsBasicBlockFirst (bb) |
| THEN |
| IF TypeCheckBecomes (p, quad) |
| THEN |
| PerformFoldBecomes (p, 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, TRUE)) |
| 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, TRUE)) |
| 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 |
| 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) ; |
| IF StrictTypeChecking AND |
| (NOT ExpressionTypeCompatible (subexprpos, "", left, right, |
| StrictTypeChecking, FALSE)) |
| THEN |
| MetaErrorT2 (subexprpos, |
| 'expression mismatch between {%1Etad} and {%2tad}', |
| left, right) ; |
| 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 |
| 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) ; |
| righttype := GetType (right) ; |
| IF StrictTypeChecking AND |
| (NOT ExpressionTypeCompatible (subexprpos, "", left, right, |
| 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 ; |
| 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 ; |
| |
| |
| (* |
| IsUnboundedArray - return TRUE if symbol is an unbounded array. |
| *) |
| |
| PROCEDURE IsUnboundedArray (sym: CARDINAL) : BOOLEAN ; |
| BEGIN |
| IF IsParameter (sym) OR IsVar (sym) |
| THEN |
| RETURN IsUnbounded (GetType (sym)) |
| END ; |
| RETURN FALSE |
| END IsUnboundedArray ; |
| |
| |
| (* |
| 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(array). |
| *) |
| |
| PROCEDURE FoldHigh (tokenno: CARDINAL; p: WalkAction; |
| quad: CARDINAL; op1, dim, array: CARDINAL) ; |
| VAR |
| t : tree ; |
| location: location_t ; |
| BEGIN |
| (* Firstly ensure that any constant literal is declared. *) |
| TryDeclareConstant (tokenno, array) ; |
| location := TokenToLocation (tokenno) ; |
| IF (NOT IsUnboundedArray (array)) AND |
| GccKnowsAbout (array) AND CompletelyResolved (array) |
| THEN |
| t := ResolveHigh (tokenno, dim, array) ; |
| (* 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, |
| we will not do this here but in CodeHigh when the result |
| can be stored. *) |
| 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) ; |
| VAR |
| constExpr, |
| overflowChecking: BOOLEAN ; |
| op : QuadOperator ; |
| tokenno, |
| left, |
| type, |
| right, |
| leftpos, |
| rightpos, |
| typepos, |
| indrxpos : CARDINAL ; |
| location : location_t ; |
| BEGIN |
| GetQuadOtok (quad, indrxpos, op, left, type, right, |
| overflowChecking, constExpr, |
| leftpos, typepos, rightpos) ; |
| tokenno := MakeVirtualTok (indrxpos, leftpos, rightpos) ; |
| location := TokenToLocation (tokenno) ; |
| |
| (* |
| Follow the Quadruple rules: |
| *) |
| DeclareConstant (rightpos, right) ; (* Checks to see whether it is a constant |
| and if necessary declare it. *) |
| DeclareConstructor (rightpos, quad, right) ; |
| IF IsConstString (right) |
| THEN |
| InternalError ('not expecting to index through a constant string') |
| ELSIF StrictTypeChecking AND |
| (NOT AssignmentTypeCompatible (indrxpos, "", left, GetType (right), TRUE)) |
| THEN |
| MetaErrorT2 (tokenno, |
| 'assignment check caught mismatch between {%1Ead} and {%2ad}', |
| left, right) ; |
| SubQuad (quad) |
| ELSE |
| |
| (* |
| Mem[op1] := Mem[Mem[op3]] |
| *) |
| BuildAssignmentStatement (location, Mod2Gcc (left), |
| BuildIndirect (location, Mod2Gcc (right), Mod2Gcc (type))) |
| 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 StrictTypeChecking AND |
| (NOT AssignmentTypeCompatible (xindrpos, "", GetType (left), right, TRUE)) |
| THEN |
| MetaErrorT2 (tokenno, |
| 'assignment check caught mismatch between {%1Ead} and {%2ad}', |
| left, right) ; |
| SubQuad (quad) |
| END ; |
| 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. |