blob: 2440b2acf66a2fa34cc6daca2e38e95d130d13e0 [file] [log] [blame]
(* 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.