(* M2SymInit.mod records initialization state for variables. Copyright (C) 2001-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . 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 . *) IMPLEMENTATION MODULE M2SymInit ; FROM Storage IMPORT ALLOCATE, DEALLOCATE ; FROM M2Debug IMPORT Assert ; FROM M2Printf IMPORT printf0, printf1, printf2, printf3, printf4 ; FROM libc IMPORT printf ; FROM NameKey IMPORT Name, NulName, KeyToCharStar, MakeKey ; FROM M2Base IMPORT Nil ; FROM M2Options IMPORT UninitVariableChecking, UninitVariableConditionalChecking, CompilerDebugging ; FROM M2MetaError IMPORT MetaErrorT1, MetaErrorStringT1, MetaErrorStringT2 ; FROM M2LexBuf IMPORT UnknownTokenNo ; FROM DynamicStrings IMPORT String, InitString, Mark, ConCat, InitString ; FROM M2Error IMPORT InternalError ; FROM M2BasicBlock IMPORT BasicBlock, InitBasicBlocks, InitBasicBlocksFromRange, KillBasicBlocks, FreeBasicBlocks, ForeachBasicBlockDo ; IMPORT Indexing ; FROM Indexing IMPORT Index ; FROM Lists IMPORT List, InitList, GetItemFromList, PutItemIntoList, IsItemInList, IncludeItemIntoList, NoOfItemsInList, RemoveItemFromList, ForeachItemInListDo, KillList, DuplicateList ; FROM SymbolTable IMPORT NulSym, ModeOfAddr, IsVar, IsRecord, GetSType, GetNth, IsRecordField, IsSet, IsArray, IsProcedure, GetVarScope, IsVarAParam, IsComponent, GetMode, VarCheckReadInit, VarInitState, PutVarInitialized, PutVarFieldInitialized, GetVarFieldInitialized, IsConst, IsConstString, NoOfParam, IsVarParam, ForeachLocalSymDo, ForeachParamSymDo, IsTemporary, ModeOfAddr, IsReallyPointer, IsUnbounded, IsVarient, IsFieldVarient, GetVarient, IsVarArrayRef, GetSymName, IsType, IsPointer, GetParameterShadowVar, IsParameter, GetLType, GetParameterHeapVar ; FROM M2Quads IMPORT QuadOperator, GetQuadOtok, GetQuad, GetNextQuad, IsNewLocalVar, IsReturn, IsKillLocalVar, IsConditional, IsUnConditional, IsBackReference, IsCall, IsGoto, GetM2OperatorDesc, Opposite, DisplayQuadRange, GetQuadTrash ; FROM M2Printf IMPORT printf0, printf1, printf2 ; FROM M2GCCDeclare IMPORT PrintSym ; CONST Debugging = FALSE ; TYPE descType = (scalar, record) ; InitDesc = POINTER TO RECORD sym, type : CARDINAL ; initialized: BOOLEAN ; CASE kind: descType OF scalar: | record: rec: recordDesc | END END ; recordDesc = RECORD fieldDesc: Indexing.Index ; END ; symAlias = POINTER TO RECORD keySym, alias : CARDINAL ; next : symAlias ; END ; bbEntry = POINTER TO RECORD start, end: CARDINAL ; (* Is this the first bb? *) first, (* Does it end with a call? *) endCall, (* Does it end with a goto? *) endGoto, (* Does it end with a conditional? *) endCond, (* Does it form part of a loop? *) topOfLoop : BOOLEAN ; trashQuad, indexBB, nextQuad, condQuad, nextBB, condBB : CARDINAL ; next : bbEntry ; END ; VAR IndirectArray, LArray : Indexing.Index ; freeList : symAlias ; bbArray : Indexing.Index ; bbFreeList : bbEntry ; ignoreList, errorList : List ; (* Ensure that we only generate one set of warnings per token. *) (* PrintSymInit - *) PROCEDURE PrintSymInit (desc: InitDesc) ; VAR i, n: CARDINAL ; BEGIN printf ("sym %d: type %d ", desc^.sym, desc^.type) ; IF desc^.kind = scalar THEN printf ("scalar") ELSE printf ("record") END ; IF NOT desc^.initialized THEN printf (" not") END ; printf (" initialized\n") ; IF (desc^.type # NulSym) AND IsRecord (desc^.type) THEN i := 1 ; n := Indexing.HighIndice (desc^.rec.fieldDesc) ; WHILE i <= n DO PrintSymInit (Indexing.GetIndice (desc^.rec.fieldDesc, i)) ; INC (i) END END END PrintSymInit ; PROCEDURE InitSymInit () : InitDesc ; VAR id: InitDesc ; BEGIN NEW (id) ; WITH id^ DO sym := NulSym ; type := NulSym ; initialized := TRUE ; kind := scalar END ; RETURN id END InitSymInit ; PROCEDURE KillSymInit (VAR desc: InitDesc) ; BEGIN WITH desc^ DO CASE kind OF record: KillFieldDesc (rec.fieldDesc) ELSE END END ; DISPOSE (desc) ; desc := NIL END KillSymInit ; PROCEDURE ConfigSymInit (desc: InitDesc; sym: CARDINAL) ; BEGIN IF IsVar (sym) OR IsRecordField (sym) THEN desc^.sym := sym ; desc^.type := GetSType (sym) ; desc^.initialized := FALSE ; IF IsRecord (desc^.type) THEN desc^.kind := record ; desc^.rec.fieldDesc := Indexing.InitIndex (1) ; PopulateFields (desc, desc^.type) ELSE desc^.kind := scalar ; IF IsArray (desc^.type) THEN desc^.initialized := TRUE (* For now we don't attempt to handle array types. *) END END END END ConfigSymInit ; (* KillFieldDesc - *) PROCEDURE KillFieldDesc (VAR fielddesc: Indexing.Index) ; VAR i, h: CARDINAL ; id : InitDesc ; BEGIN i := 1 ; h := Indexing.HighIndice (fielddesc) ; WHILE i <= h DO id := Indexing.GetIndice (fielddesc, i) ; KillSymInit (id) ; INC (i) END ; fielddesc := Indexing.KillIndex (fielddesc) END KillFieldDesc ; (* PopulateFields - *) PROCEDURE PopulateFields (desc: InitDesc; recsym: CARDINAL) ; VAR field, i : CARDINAL ; fdesc: InitDesc ; BEGIN Assert (IsRecord (recsym)) ; i := 1 ; REPEAT field := GetNth (recsym, i) ; IF field # NulSym THEN fdesc := InitSymInit () ; ConfigSymInit (fdesc, field) ; Indexing.IncludeIndiceIntoIndex (desc^.rec.fieldDesc, fdesc) ; INC (i) END UNTIL field = NulSym END PopulateFields ; PROCEDURE SetInitialized (desc: InitDesc) ; BEGIN desc^.initialized := TRUE END SetInitialized ; PROCEDURE GetInitialized (desc: InitDesc) : BOOLEAN ; BEGIN IF NOT desc^.initialized THEN IF IsRecord (desc^.type) THEN TrySetInitialized (desc) END END ; IF Debugging THEN PrintSymInit (desc) END ; RETURN desc^.initialized END GetInitialized ; PROCEDURE GetFieldDesc (desc: InitDesc; field: CARDINAL) : InitDesc ; VAR fsym, i : CARDINAL ; BEGIN IF IsRecord (desc^.type) THEN i := 1 ; REPEAT fsym := GetNth (desc^.type, i) ; IF field = fsym THEN RETURN Indexing.GetIndice (desc^.rec.fieldDesc, i) END ; INC (i) UNTIL fsym = NulSym END ; RETURN NIL END GetFieldDesc ; PROCEDURE SetFieldInitialized (desc: InitDesc; fieldlist: List) : BOOLEAN ; BEGIN RETURN SetFieldInitializedNo (desc, fieldlist, 1) END SetFieldInitialized ; (* TrySetInitialized - *) PROCEDURE TrySetInitialized (desc: InitDesc) ; VAR i, h : CARDINAL ; fdesc: InitDesc ; BEGIN h := Indexing.HighIndice (desc^.rec.fieldDesc) ; i := 1 ; WHILE i <= h DO fdesc := Indexing.GetIndice (desc^.rec.fieldDesc, i) ; IF NOT fdesc^.initialized THEN RETURN END ; INC (i) END ; desc^.initialized := TRUE END TrySetInitialized ; (* SetFieldInitializedNo - *) PROCEDURE SetFieldInitializedNo (desc: InitDesc; fieldlist: List; level: CARDINAL) : BOOLEAN ; VAR init : BOOLEAN ; nsym : CARDINAL ; fdesc: InitDesc ; BEGIN IF level > NoOfItemsInList (fieldlist) THEN RETURN FALSE ELSE nsym := GetItemFromList (fieldlist, level) ; fdesc := GetFieldDesc (desc, nsym) ; IF fdesc = NIL THEN RETURN FALSE ELSIF level = NoOfItemsInList (fieldlist) THEN SetInitialized (fdesc) ; TrySetInitialized (desc) ; RETURN desc^.initialized ELSE init := SetFieldInitializedNo (fdesc, fieldlist, level + 1) ; TrySetInitialized (desc) ; RETURN desc^.initialized END END END SetFieldInitializedNo ; PROCEDURE GetFieldInitialized (desc: InitDesc; fieldlist: List) : BOOLEAN ; BEGIN RETURN GetFieldInitializedNo (desc, fieldlist, 1) END GetFieldInitialized ; PROCEDURE GetFieldInitializedNo (desc: InitDesc; fieldlist: List; level: CARDINAL) : BOOLEAN ; VAR nsym : CARDINAL ; fdesc: InitDesc ; BEGIN IF desc^.initialized THEN RETURN TRUE ELSIF level > NoOfItemsInList (fieldlist) THEN RETURN FALSE ELSE nsym := GetItemFromList (fieldlist, level) ; fdesc := GetFieldDesc (desc, nsym) ; IF fdesc = NIL THEN (* The pointer variable maybe uninitialized and hence we cannot find the record variable. *) RETURN FALSE ELSIF fdesc^.initialized THEN RETURN TRUE ELSE RETURN GetFieldInitializedNo (fdesc, fieldlist, level + 1) END END END GetFieldInitializedNo ; (* IsGlobalVar - *) PROCEDURE IsGlobalVar (sym: CARDINAL) : BOOLEAN ; BEGIN RETURN IsVar (sym) AND (NOT IsProcedure (GetVarScope (sym))) END IsGlobalVar ; (* IsLocalVar - *) PROCEDURE IsLocalVar (procsym, varsym: CARDINAL) : BOOLEAN ; BEGIN RETURN IsVar (varsym) AND (GetVarScope (varsym) = procsym) END IsLocalVar ; (* RecordFieldContainsVarient - *) PROCEDURE RecordFieldContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN Assert (IsRecordField (sym)) ; IF doContainsVariant (GetSType (sym), visited) THEN RETURN TRUE END ; RETURN GetVarient (sym) # NulSym END RecordFieldContainsVarient ; (* RecordContainsVarient - *) PROCEDURE RecordContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; VAR i, fieldsym, fieldtype: CARDINAL ; BEGIN Assert (IsRecord (sym)) ; i := 1 ; REPEAT fieldsym := GetNth (sym, i) ; IF fieldsym # NulSym THEN IF IsRecordField (fieldsym) THEN IF RecordFieldContainsVarient (fieldsym, visited) THEN RETURN TRUE END ELSIF IsVarient (fieldsym) THEN RETURN TRUE END ; INC (i) END UNTIL fieldsym = NulSym ; RETURN FALSE END RecordContainsVarient ; (* VarContainsVarient - *) PROCEDURE VarContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN Assert (IsVar (sym)) ; RETURN doContainsVariant (GetSType (sym), visited) END VarContainsVarient ; (* TypeContainsVarient - *) PROCEDURE TypeContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN Assert (IsType (sym)) ; RETURN doContainsVariant (GetSType (sym), visited) END TypeContainsVarient ; (* ArrayContainsVarient - *) PROCEDURE ArrayContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN Assert (IsArray (sym)) ; RETURN doContainsVariant (GetSType (sym), visited) END ArrayContainsVarient ; (* PointerContainsVarient - *) PROCEDURE PointerContainsVarient (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN Assert (IsPointer (sym)) ; RETURN doContainsVariant (GetSType (sym), visited) END PointerContainsVarient ; (* doContainsVariant - *) PROCEDURE doContainsVariant (sym: CARDINAL; visited: List) : BOOLEAN ; BEGIN IF (sym # NulSym) AND (NOT IsItemInList (visited, sym)) THEN IncludeItemIntoList (visited, sym) ; IF IsVar (sym) THEN RETURN VarContainsVarient (sym, visited) ELSIF IsRecord (sym) THEN RETURN RecordContainsVarient (sym, visited) ELSIF IsPointer (sym) THEN RETURN PointerContainsVarient (sym, visited) ELSIF IsArray (sym) THEN RETURN ArrayContainsVarient (sym, visited) ELSIF IsType (sym) THEN RETURN TypeContainsVarient (sym, visited) END END ; RETURN FALSE END doContainsVariant ; (* ContainsVariant - returns TRUE if type sym contains a variant record. *) PROCEDURE ContainsVariant (sym: CARDINAL) : BOOLEAN ; VAR visited: List ; result : BOOLEAN ; BEGIN InitList (visited) ; result := doContainsVariant (sym, visited) ; KillList (visited) ; RETURN result END ContainsVariant ; (* IssueConditional - *) PROCEDURE IssueConditional (quad: CARDINAL; conditional: BOOLEAN) ; VAR op : QuadOperator ; op1, op2, op3 : CARDINAL ; op1tok, op2tok, op3tok, qtok: CARDINAL ; overflowChecking : BOOLEAN ; s : String ; BEGIN GetQuadOtok (quad, qtok, op, op1, op2, op3, overflowChecking, op1tok, op2tok, op3tok) ; IF IsUniqueWarning (qtok) THEN op1tok := DefaultTokPos (op1tok, qtok) ; op2tok := DefaultTokPos (op2tok, qtok) ; op3tok := DefaultTokPos (op3tok, qtok) ; IF NOT conditional THEN op := Opposite (op) END ; s := InitString ('depending upon the result of {%1Oad} ') ; s := ConCat (s, Mark (GetM2OperatorDesc (op))) ; s := ConCat (s, InitString (' {%2ad}')) ; MetaErrorStringT2 (qtok, s, op1, op2) END END IssueConditional ; (* GenerateNoteFlow - *) PROCEDURE GenerateNoteFlow (lst: List; n: CARDINAL; warning: BOOLEAN) ; VAR i : CARDINAL ; ip1Ptr, iPtr : bbEntry ; BEGIN IF NOT warning THEN (* Only issue flow messages for non warnings. *) i := 1 ; WHILE i <= n DO iPtr := Indexing.GetIndice (bbArray, i) ; IF iPtr^.endCond THEN IF i < n THEN ip1Ptr := Indexing.GetIndice (bbArray, i+1) ; IssueConditional (iPtr^.end, iPtr^.condBB = ip1Ptr^.indexBB) END END ; INC (i) END END END GenerateNoteFlow ; (* IssueWarning - issue a warning or note at tok location. *) PROCEDURE IssueWarning (tok: CARDINAL; before, after: ARRAY OF CHAR; sym: CARDINAL; warning: BOOLEAN) ; VAR s: String ; BEGIN s := InitString (before) ; IF warning THEN s := ConCat (s, Mark (InitString ('{%1Wad}'))) ELSE s := ConCat (s, Mark (InitString ('{%1Oad}'))) END ; s := ConCat (s, Mark (InitString (after))) ; MetaErrorStringT1 (tok, s, sym) END IssueWarning ; (* IsUniqueWarning - return TRUE if a warning has not been issued at tok. It remembers tok and subsequent calls will always return FALSE. *) PROCEDURE IsUniqueWarning (tok: CARDINAL) : BOOLEAN ; BEGIN IF NOT IsItemInList (errorList, tok) THEN IncludeItemIntoList (errorList, tok) ; RETURN TRUE ELSE RETURN FALSE END END IsUniqueWarning ; (* CheckDeferredRecordAccess - *) PROCEDURE CheckDeferredRecordAccess (procsym: CARDINAL; tok: CARDINAL; sym: CARDINAL; canDereference, warning: BOOLEAN; lst: List; i: CARDINAL) ; VAR unique: BOOLEAN ; BEGIN IF IsVar (sym) THEN IF Debugging THEN Trace ("CheckDeferredRecordAccess %d\n", sym) ; PrintSym (sym) ; IF canDereference THEN printf1 ("checkReadInit (%d, true)\n", sym) ELSE printf1 ("checkReadInit (%d, false)\n", sym) END END ; IF IsExempt (sym) THEN Trace ("checkReadInit sym is a parameter or not a local variable (%d)", sym) ; (* We assume parameters have been initialized. *) PutVarInitialized (sym, LeftValue) ; PutVarInitialized (sym, RightValue) (* SetVarInitialized (sym, TRUE) *) ELSIF IsUnbounded (GetSType (sym)) THEN SetVarInitialized (sym, TRUE, tok) ELSIF IsComponent (sym) THEN Trace ("checkReadInit IsComponent (%d) is true)", sym) ; IF (NOT GetVarComponentInitialized (sym, tok)) AND IsUniqueWarning (tok) THEN GenerateNoteFlow (lst, i, warning) ; IssueWarning (tok, 'attempting to access ', ' before it has been initialized', sym, warning) END ELSIF (GetMode (sym) = LeftValue) AND canDereference THEN Trace ("checkReadInit GetMode (%d) = LeftValue and canDereference (LeftValue and RightValue VarCheckReadInit)", sym) ; unique := TRUE ; IF NOT VarCheckReadInit (sym, LeftValue) THEN unique := IsUniqueWarning (tok) ; IF unique THEN GenerateNoteFlow (lst, i, warning) ; IssueWarning (tok, 'attempting to access the address of ', ' before it has been initialized', sym, warning) END END ; IF NOT VarCheckReadInit (sym, RightValue) THEN IF unique THEN GenerateNoteFlow (lst, i, warning) ; IssueWarning (tok, 'attempting to access ', ' before it has been initialized', sym, warning) END END ELSE Trace ("checkReadInit call VarCheckReadInit using GetMode (%d)", sym) ; IF (NOT VarCheckReadInit (sym, GetMode (sym))) AND IsUniqueWarning (tok) THEN GenerateNoteFlow (lst, i, warning) ; IssueWarning (tok, 'attempting to access ', ' before it has been initialized', sym, warning) END END END END CheckDeferredRecordAccess ; (* SetVarUninitialized - resets variable init state. *) PROCEDURE SetVarUninitialized (sym: CARDINAL) ; BEGIN IF IsVar (sym) THEN IF NOT IsUnbounded (GetSType (sym)) THEN VarInitState (sym) END END END SetVarUninitialized ; (* ComponentFindVar - *) PROCEDURE ComponentFindVar (sym: CARDINAL; VAR lvalue: BOOLEAN; tok: CARDINAL) : CARDINAL ; VAR nsym, i : CARDINAL ; BEGIN i := 1 ; REPEAT nsym := GetNth (sym, i) ; lvalue := GetMode (nsym) = LeftValue ; nsym := getLAlias (nsym) ; IF nsym = Nil THEN MetaErrorT1 (tok, "attempting to dereference {%1Wad} which will be a {%kNIL} pointer", sym) ; RETURN NulSym ELSIF (nsym # NulSym) AND IsVar (nsym) THEN IF (nsym # sym) AND IsComponent (nsym) THEN RETURN ComponentFindVar (nsym, lvalue, tok) ELSE RETURN nsym END END ; INC (i) UNTIL nsym = NulSym ; RETURN NulSym END ComponentFindVar ; (* ComponentCreateFieldList - builds a list of fields accessed by the component var. Each item in the list will be a field of incremental levels though a nested record. It is not a list of fields at the same level. foo = RECORD v: RECORD x, y: CARDINAL ; END ; w: CARDINAL ; END ; { v, x } for example and not { v, w } *) PROCEDURE ComponentCreateFieldList (sym: CARDINAL) : List ; VAR lst: List ; BEGIN InitList (lst) ; IF IsVar (sym) AND IsComponent (sym) THEN ComponentBuildFieldList (lst, sym) END ; RETURN lst END ComponentCreateFieldList ; PROCEDURE ComponentBuildFieldList (lst: List; sym: CARDINAL) ; VAR i, nsym: CARDINAL ; BEGIN i := 1 ; REPEAT nsym := GetNth (sym, i) ; IF nsym # NulSym THEN IF IsComponent (nsym) THEN ComponentBuildFieldList (lst, nsym) ELSIF IsRecordField (nsym) THEN IncludeItemIntoList (lst, nsym) END ; INC (i) END UNTIL nsym = NulSym END ComponentBuildFieldList ; (* deRefComponent - *) PROCEDURE deRefComponent (component: CARDINAL; lvalue: BOOLEAN; sym: CARDINAL; tok: CARDINAL) : CARDINAL ; BEGIN IF lvalue THEN RETURN getContent (component, sym, tok) ELSE RETURN component END END deRefComponent ; (* SetVarComponentInitialized - *) PROCEDURE SetVarComponentInitialized (sym: CARDINAL; tok: CARDINAL) ; VAR lvalue: BOOLEAN ; i, n, fsym, vsym : CARDINAL ; lst : List ; BEGIN vsym := ComponentFindVar (sym, lvalue, tok) ; vsym := deRefComponent (vsym, lvalue, sym, tok) ; IF vsym # NulSym THEN IF Debugging THEN printf0 ("*************** vsym is: ") ; PrintSym (vsym) END ; (* Build list accessing the field. *) lst := ComponentCreateFieldList (sym) ; IF Debugging THEN printf2 ("sym = %d, vsym = %d, fields:", sym, vsym) END ; (* Now mark this field in the record variable as initialized. *) IF PutVarFieldInitialized (vsym, RightValue, lst) THEN IF Debugging THEN i := 1 ; n := NoOfItemsInList (lst) ; WHILE i <= n DO fsym := GetItemFromList (lst, i) ; printf1 (" %d", fsym) ; INC (i) END ; printf0 (" is initialized\n") END ELSIF Debugging THEN printf0 (" vsym is not a var\n") END ; KillList (lst) END END SetVarComponentInitialized ; (* GetVarComponentInitialized - *) PROCEDURE GetVarComponentInitialized (sym: CARDINAL; tok: CARDINAL) : BOOLEAN ; VAR lvalue, init : BOOLEAN ; component, vsym : CARDINAL ; lst : List ; BEGIN component := ComponentFindVar (sym, lvalue, tok) ; IF IsItemInList (ignoreList, component) OR IsExempt (component) THEN RETURN TRUE ELSE init := FALSE ; vsym := deRefComponent (component, lvalue, sym, tok) ; IF vsym # NulSym THEN IF IsExempt (vsym) THEN init := TRUE ELSE (* Create list representing how the field is accessed. *) lst := ComponentCreateFieldList (sym) ; (* Now obtain the mark indicating whether this field was initialized. *) init := GetVarFieldInitialized (vsym, RightValue, lst) ; KillList (lst) END END ; RETURN init END END GetVarComponentInitialized ; (* Trace - *) PROCEDURE Trace (message: ARRAY OF CHAR; sym: CARDINAL) ; BEGIN IF Debugging THEN printf1 (message, sym) ; printf0 ("\n") END END Trace ; (* SetVarInitialized - if the variable has a left mode and can be dereferenced then set the left and right initialization state. *) PROCEDURE SetVarInitialized (sym: CARDINAL; canDereference: BOOLEAN; tok: CARDINAL) ; BEGIN IF IsVar (sym) THEN RemoveItemFromList (ignoreList, sym) ; IF IsComponent (sym) THEN Trace ("SetVarInitialized sym %d is a component and calling SetVarComponentInitialized", sym); SetVarComponentInitialized (sym, tok) ELSIF (GetMode (sym) = LeftValue) AND canDereference THEN Trace ("SetVarInitialized sym %d is LeftValue and canDeference and calling PutVarInitialized LeftValue and RightValue", sym); PutVarInitialized (sym, LeftValue) ; PutVarInitialized (sym, RightValue) ELSE Trace ("SetVarInitialized sym %d calling PutVarInitialized with its mode", sym); PutVarInitialized (sym, GetMode (sym)) END ; IF Debugging THEN PrintSym (sym) END END END SetVarInitialized ; (* doGetVarInitialized - *) PROCEDURE doGetVarInitialized (sym: CARDINAL; tok: CARDINAL) : BOOLEAN ; BEGIN IF IsVar (sym) THEN IF IsUnbounded (GetSType (sym)) THEN RETURN TRUE ELSIF IsComponent (sym) THEN RETURN GetVarComponentInitialized (sym, tok) END ; RETURN VarCheckReadInit (sym, GetMode (sym)) END ; RETURN IsConst (sym) AND IsConstString (sym) END doGetVarInitialized ; (* GetVarInitialized - *) PROCEDURE GetVarInitialized (sym: CARDINAL; tok: CARDINAL) : BOOLEAN ; VAR init: BOOLEAN ; BEGIN init := doGetVarInitialized (sym, tok) ; IF Debugging THEN IF init THEN Trace ("GetVarInitialized (sym = %d) returning TRUE", sym) ELSE Trace ("GetVarInitialized (sym = %d) returning FALSE", sym) END END ; RETURN init END GetVarInitialized ; (* IsExempt - returns TRUE if sym is a global variable or a parameter or a variable with a variant record type. *) PROCEDURE IsExempt (sym: CARDINAL) : BOOLEAN ; BEGIN RETURN (sym # NulSym) AND IsVar (sym) AND (IsGlobalVar (sym) OR (* (IsVarAParam (sym) AND (GetMode (sym) = LeftValue)) OR *) ContainsVariant (sym) OR IsArray (GetSType (sym)) OR IsSet (GetSType (sym)) OR IsUnbounded (GetSType (sym)) OR IsVarArrayRef (sym) OR IsItemInList (ignoreList, sym)) END IsExempt ; (* CheckBinary - *) PROCEDURE CheckBinary (procSym, op1tok, op1, op2tok, op2, op3tok, op3: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) ; BEGIN CheckDeferredRecordAccess (procSym, op2tok, op2, FALSE, warning, lst, i) ; CheckDeferredRecordAccess (procSym, op3tok, op3, FALSE, warning, lst, i) ; SetVarInitialized (op1, FALSE, op1tok) END CheckBinary ; (* CheckUnary - *) PROCEDURE CheckUnary (procSym, lhstok, lhs, rhstok, rhs: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) ; BEGIN CheckDeferredRecordAccess (procSym, rhstok, rhs, FALSE, warning, lst, i) ; SetVarInitialized (lhs, FALSE, lhstok) END CheckUnary ; (* CheckXIndr - *) PROCEDURE CheckXIndr (procSym, lhstok, lhs, type, rhstok, rhs: CARDINAL; warning: BOOLEAN; bblst: List; i: CARDINAL) ; VAR lst : List ; content: CARDINAL ; BEGIN CheckDeferredRecordAccess (procSym, rhstok, rhs, FALSE, warning, bblst, i) ; CheckDeferredRecordAccess (procSym, lhstok, lhs, FALSE, warning, bblst, i) ; (* Now see if we know what lhs is pointing to and set fields if necessary. *) content := getContent (getLAlias (lhs), lhs, lhstok) ; IF (content # NulSym) AND (content # lhs) AND (GetSType (content) = type) THEN IF IsReallyPointer (rhs) THEN SetupLAlias (content, rhs) END ; IF IsRecord (type) THEN (* Set all fields of content as initialized. *) SetVarInitialized (content, FALSE, lhstok) ELSE (* Set only the field assigned in vsym as initialized. *) lst := ComponentCreateFieldList (rhs) ; IF PutVarFieldInitialized (content, RightValue, lst) THEN END ; KillList (lst) END END END CheckXIndr ; (* CheckIndrX - *) PROCEDURE CheckIndrX (procSym, lhstok, lhs, type, rhstok, rhs: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) ; VAR content: CARDINAL ; BEGIN CheckDeferredRecordAccess (procSym, rhstok, rhs, FALSE, warning, lst, i) ; content := getContent (getLAlias (rhs), rhs, rhstok) ; IF content = NulSym THEN IncludeItemIntoList (ignoreList, lhs) ELSE CheckDeferredRecordAccess (procSym, rhstok, content, TRUE, warning, lst, i) ; SetVarInitialized (lhs, VarCheckReadInit (content, RightValue), lhstok) ; IF IsReallyPointer (content) THEN SetupLAlias (lhs, content) END END END CheckIndrX ; (* CheckRecordField - *) PROCEDURE CheckRecordField (procSym, op1tok, op1, op2tok, op2: CARDINAL) ; BEGIN PutVarInitialized (op1, LeftValue) END CheckRecordField ; (* CheckBecomes - *) PROCEDURE CheckBecomes (procSym, destok, des, exprtok, expr: CARDINAL; warning: BOOLEAN; bblst: List; i: CARDINAL) ; VAR lvalue: BOOLEAN ; lst : List ; vsym : CARDINAL ; BEGIN CheckDeferredRecordAccess (procSym, exprtok, expr, FALSE, warning, bblst, i) ; SetupLAlias (des, expr) ; SetVarInitialized (des, FALSE, destok) ; (* Now see if we know what lhs is pointing to and set fields if necessary. *) IF IsComponent (des) THEN vsym := ComponentFindVar (des, lvalue, destok) ; vsym := deRefComponent (vsym, lvalue, des, destok) ; IF vsym # NulSym THEN (* Set only the field assigned in vsym as initialized. *) lst := ComponentCreateFieldList (des) ; IF PutVarFieldInitialized (vsym, RightValue, lst) THEN END ; KillList (lst) END END END CheckBecomes ; (* CheckComparison - *) PROCEDURE CheckComparison (procSym, op1tok, op1, op2tok, op2: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) ; BEGIN CheckDeferredRecordAccess (procSym, op1tok, op1, FALSE, warning, lst, i) ; CheckDeferredRecordAccess (procSym, op2tok, op2, FALSE, warning, lst, i) END CheckComparison ; (* CheckAddr - *) PROCEDURE CheckAddr (procSym, ptrtok, ptr, contenttok, content: CARDINAL) ; BEGIN SetVarInitialized (ptr, GetVarInitialized (content, contenttok), ptrtok) ; SetupIndr (ptr, content) END CheckAddr ; (* DefaultTokPos - *) PROCEDURE DefaultTokPos (preferredPos, defaultPos: CARDINAL) : CARDINAL ; BEGIN IF preferredPos = UnknownTokenNo THEN RETURN defaultPos END ; RETURN preferredPos END DefaultTokPos ; (* stop - *) PROCEDURE stop ; END stop ; (* CheckReadBeforeInitQuad - *) PROCEDURE CheckReadBeforeInitQuad (procSym: CARDINAL; quad: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) : BOOLEAN ; VAR op : QuadOperator ; op1, op2, op3 : CARDINAL ; op1tok, op2tok, op3tok, qtok: CARDINAL ; overflowChecking : BOOLEAN ; BEGIN IF quad = 3140 THEN stop END ; IF Debugging THEN printf1 ("CheckReadBeforeInitQuad (quad %d)\n", quad) ; DumpAliases ; ForeachLocalSymDo (procSym, PrintSym) ; printf0 ("***********************************\n") END ; GetQuadOtok (quad, qtok, op, op1, op2, op3, overflowChecking, op1tok, op2tok, op3tok) ; op1tok := DefaultTokPos (op1tok, qtok) ; op2tok := DefaultTokPos (op2tok, qtok) ; op3tok := DefaultTokPos (op3tok, qtok) ; CASE op OF (* Jumps, calls and branches. *) IfInOp, IfNotInOp, IfEquOp, IfNotEquOp, IfLessOp, IfLessEquOp, IfGreOp, IfGreEquOp : CheckComparison (procSym, op1tok, op1, op2tok, op2, warning, lst, i) | TryOp, ReturnOp, CallOp, KillLocalVarOp, RetryOp, GotoOp : RETURN TRUE | (* End of basic block. *) (* Variable references. *) InclOp, ExclOp : CheckDeferredRecordAccess (procSym, op1tok, op1, FALSE, warning, lst, i) ; CheckDeferredRecordAccess (procSym, op1tok, op1, TRUE, warning, lst, i) ; CheckDeferredRecordAccess (procSym, op3tok, op3, FALSE, warning, lst, i) | NegateOp : CheckUnary (procSym, op1tok, op1, op3tok, op3, warning, lst, i) | BecomesOp : CheckBecomes (procSym, op1tok, op1, op3tok, op3, warning, lst, i) | UnboundedOp, FunctValueOp, StandardFunctionOp, HighOp, SizeOp : SetVarInitialized (op1, FALSE, op1tok) | AddrOp : CheckAddr (procSym, op1tok, op1, op3tok, op3) | ReturnValueOp : SetVarInitialized (op1, FALSE, op1tok) | NewLocalVarOp : | ParamOp : CheckDeferredRecordAccess (procSym, op2tok, op2, FALSE, warning, lst, i) ; CheckDeferredRecordAccess (procSym, op3tok, op3, FALSE, warning, lst, i) ; IF (op1 > 0) AND (op1 <= NoOfParam (op2)) AND IsVarParam (op2, op1) THEN SetVarInitialized (op3, TRUE, op3tok) END | ArrayOp : CheckDeferredRecordAccess (procSym, op3tok, op3, FALSE, warning, lst, i) ; SetVarInitialized (op1, TRUE, op1tok) | RecordFieldOp : CheckRecordField (procSym, op1tok, op1, op2tok, op2) | LogicalShiftOp, LogicalRotateOp, LogicalOrOp, LogicalAndOp, LogicalXorOp, LogicalDiffOp, CoerceOp, ConvertOp, CastOp, AddOp, ArithAddOp, SubOp, MultOp, DivM2Op, ModM2Op, ModFloorOp, DivCeilOp, ModCeilOp, DivFloorOp, ModTruncOp, DivTruncOp : CheckBinary (procSym, op1tok, op1, op2tok, op2, op3tok, op3, warning, lst, i) | XIndrOp : CheckXIndr (procSym, op1tok, op1, op2, op3tok, op3, warning, lst, i) | IndrXOp : CheckIndrX (procSym, op1tok, op1, op2, op3tok, op3, warning, lst, i) | SaveExceptionOp : SetVarInitialized (op1, FALSE, op1tok) | RestoreExceptionOp: CheckDeferredRecordAccess (procSym, op1tok, op1, FALSE, warning, lst, i) | SubrangeLowOp, SubrangeHighOp : InternalError ('quadruples should have been resolved') | ElementSizeOp, BuiltinConstOp, (* Nothing to do, it is assigning a constant to op1 (also a const). *) BuiltinTypeInfoOp, (* Likewise assigning op1 (const) with a type. *) ProcedureScopeOp, InitEndOp, InitStartOp, FinallyStartOp, FinallyEndOp, CatchBeginOp, CatchEndOp, ThrowOp, StartDefFileOp, StartModFileOp, EndFileOp, CodeOnOp, CodeOffOp, ProfileOnOp, ProfileOffOp, OptimizeOnOp, OptimizeOffOp, InlineOp, LineNumberOp, StatementNoteOp, SavePriorityOp, RestorePriorityOp, RangeCheckOp, ModuleScopeOp, ErrorOp, DummyOp, OptParamOp, InitAddressOp : | END ; RETURN FALSE END CheckReadBeforeInitQuad ; (* FilterCheckReadBeforeInitQuad - *) PROCEDURE FilterCheckReadBeforeInitQuad (procSym: CARDINAL; start: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) : BOOLEAN ; VAR Op : QuadOperator ; Op1, Op2, Op3: CARDINAL ; BEGIN GetQuad (start, Op, Op1, Op2, Op3) ; IF (Op # RangeCheckOp) AND (Op # StatementNoteOp) THEN RETURN CheckReadBeforeInitQuad (procSym, start, warning, lst, i) END ; RETURN FALSE END FilterCheckReadBeforeInitQuad ; (* CheckReadBeforeInitFirstBasicBlock - *) PROCEDURE CheckReadBeforeInitFirstBasicBlock (procSym: CARDINAL; start, end: CARDINAL; warning: BOOLEAN; lst: List; i: CARDINAL) ; BEGIN LOOP IF FilterCheckReadBeforeInitQuad (procSym, start, warning, lst, i) THEN END ; IF start = end THEN RETURN ELSE start := GetNextQuad (start) END END END CheckReadBeforeInitFirstBasicBlock ; (* bbArrayKill - *) PROCEDURE bbArrayKill ; VAR i, h : CARDINAL ; bbPtr: bbEntry ; BEGIN h := Indexing.HighIndice (bbArray) ; i := 1 ; WHILE i <= h DO bbPtr := Indexing.GetIndice (bbArray, i) ; bbPtr^.next := bbFreeList ; bbFreeList := bbPtr ; INC (i) END ; bbArray := Indexing.KillIndex (bbArray) END bbArrayKill ; (* DumpBBEntry - *) PROCEDURE DumpBBEntry (bbPtr: bbEntry; procSym: CARDINAL) ; BEGIN printf4 ("bb %d: scope %d: quads: %d .. %d", bbPtr^.indexBB, procSym, bbPtr^.start, bbPtr^.end) ; IF bbPtr^.first THEN printf0 (" first") END ; IF bbPtr^.endCall THEN printf0 (" endcall") END ; IF bbPtr^.endGoto THEN printf0 (" endgoto") END ; IF bbPtr^.endCond THEN printf0 (" endcond") END ; IF bbPtr^.topOfLoop THEN printf0 (" topofloop") END ; IF bbPtr^.condBB # 0 THEN printf1 (" cond %d", bbPtr^.condBB) END ; IF bbPtr^.nextBB # 0 THEN printf1 (" next %d", bbPtr^.nextBB) END ; printf0 ("\n") END DumpBBEntry ; (* DumpBBArray - *) PROCEDURE DumpBBArray (procSym: CARDINAL) ; VAR bbPtr: bbEntry ; i, n : CARDINAL ; BEGIN i := 1 ; n := Indexing.HighIndice (bbArray) ; WHILE i <= n DO bbPtr := Indexing.GetIndice (bbArray, i) ; DumpBBEntry (bbPtr, procSym) ; INC (i) END ; i := 1 ; WHILE i <= n DO bbPtr := Indexing.GetIndice (bbArray, i) ; printf4 ("bb %d: scope %d: quads: %d .. %d\n", bbPtr^.indexBB, procSym, bbPtr^.start, bbPtr^.end) ; DisplayQuadRange (procSym, bbPtr^.start, bbPtr^.end) ; INC (i) END END DumpBBArray ; (* DumpBBSequence - *) PROCEDURE DumpBBSequence (procSym: CARDINAL; lst: List) ; VAR arrayindex, listindex, n: CARDINAL ; BEGIN n := NoOfItemsInList (lst) ; listindex := 1 ; printf0 ("=============\n"); printf0 (" checking sequence:"); WHILE listindex <= n DO arrayindex := GetItemFromList (lst, listindex) ; printf1 (" [%d]", listindex) ; INC (listindex) END ; printf0 ("\n") END DumpBBSequence ; (* trashParam - *) PROCEDURE trashParam (trashQuad: CARDINAL) ; VAR op : QuadOperator ; op1, proc, param, paramValue : CARDINAL ; op1tok, op2tok, paramtok, qtok: CARDINAL ; overflowChecking : BOOLEAN ; heapValue, ptrToHeap : CARDINAL ; BEGIN IF trashQuad # 0 THEN GetQuadOtok (trashQuad, qtok, op, op1, proc, param, overflowChecking, op1tok, op2tok, paramtok) ; heapValue := GetQuadTrash (trashQuad) ; IF Debugging THEN printf1 ("heapValue = %d\n", heapValue) END ; IF heapValue # NulSym THEN SetVarInitialized (param, FALSE, paramtok) ; paramValue := getLAlias (param) ; ptrToHeap := getContent (paramValue, param, paramtok) ; IF ptrToHeap # NulSym THEN IF IsDeallocate (proc) THEN SetupLAlias (ptrToHeap, Nil) ; SetVarInitialized (ptrToHeap, FALSE, paramtok) ELSE SetupIndr (ptrToHeap, heapValue) ; SetVarInitialized (ptrToHeap, TRUE, paramtok) END END END END ; DumpAliases END trashParam ; (* SetVarLRInitialized - this sets up an alias between the parameter value and the pointer for the case: procedure foo (var shadow: PtrToType) ; which allows shadow to be statically analyzed once it is re-assigned. *) PROCEDURE SetVarLRInitialized (param: CARDINAL) ; VAR heap, shadow: CARDINAL ; BEGIN Assert (IsParameter (param)) ; shadow := GetParameterShadowVar (param) ; IF shadow # NulSym THEN IncludeItemIntoList (ignoreList, shadow) END ; heap := GetParameterHeapVar (param) ; IF (shadow # NulSym) AND (heap # NulSym) THEN PutVarInitialized (shadow, GetMode (shadow)) ; PutVarInitialized (heap, GetMode (heap)) ; SetupIndr (shadow, heap) ; IncludeItemIntoList (ignoreList, heap) END END SetVarLRInitialized ; (* TestBBSequence - *) PROCEDURE TestBBSequence (procSym: CARDINAL; lst: List) ; VAR bbPtr : bbEntry ; bbi, i, n : CARDINAL ; warning: BOOLEAN ; (* Should we issue a warning rather than a note? *) BEGIN IF Debugging THEN DumpBBSequence (procSym, lst) END ; initBlock ; ForeachLocalSymDo (procSym, SetVarUninitialized) ; ForeachParamSymDo (procSym, SetVarLRInitialized) ; n := NoOfItemsInList (lst) ; i := 1 ; warning := TRUE ; WHILE i <= n DO bbi := GetItemFromList (lst, i) ; bbPtr := Indexing.GetIndice (bbArray, bbi) ; CheckReadBeforeInitFirstBasicBlock (procSym, bbPtr^.start, bbPtr^.end, warning, lst, i) ; IF bbPtr^.endCond THEN (* Check to see if we are moving into an conditional block in which case we will issue a note. *) warning := FALSE ELSIF bbPtr^.endCall AND (bbPtr^.trashQuad # 0) THEN trashParam (bbPtr^.trashQuad) END ; INC (i) END ; killBlock END TestBBSequence ; (* CreateBBPermultations - *) PROCEDURE CreateBBPermultations (procSym: CARDINAL; i: CARDINAL; lst: List) ; VAR duplst: List ; iPtr : bbEntry ; BEGIN IF i = 0 THEN TestBBSequence (procSym, lst) ELSE iPtr := Indexing.GetIndice (bbArray, i) ; IF iPtr^.topOfLoop THEN TestBBSequence (procSym, lst) ELSE duplst := DuplicateList (lst) ; IncludeItemIntoList (duplst, i) ; IF iPtr^.endCall AND (iPtr^.trashQuad = 0) THEN TestBBSequence (procSym, duplst) ELSIF iPtr^.endGoto THEN CreateBBPermultations (procSym, iPtr^.nextBB, duplst) ELSIF UninitVariableConditionalChecking AND iPtr^.endCond THEN CreateBBPermultations (procSym, iPtr^.nextBB, duplst) ; CreateBBPermultations (procSym, iPtr^.condBB, duplst) ELSIF iPtr^.endCond THEN TestBBSequence (procSym, duplst) ELSE (* Fall through. *) CreateBBPermultations (procSym, iPtr^.nextBB, duplst) END ; KillList (duplst) END END END CreateBBPermultations ; (* ScopeBlockVariableAnalysis - checks to see whether a variable is read before it has been initialized. *) PROCEDURE ScopeBlockVariableAnalysis (Scope: CARDINAL; Start, End: CARDINAL) ; VAR bb : BasicBlock ; lst: List ; BEGIN IF UninitVariableChecking THEN bbArray := Indexing.InitIndex (1) ; bb := InitBasicBlocksFromRange (Scope, Start, End) ; ForeachBasicBlockDo (bb, AppendEntry) ; KillBasicBlocks (bb) ; GenerateCFG ; IF Scope # NulSym THEN InitList (lst) ; IF Debugging THEN DumpBBArray (Scope) ; IF UninitVariableConditionalChecking THEN printf0 ("UninitVariableConditionalChecking is TRUE\n") END END ; CreateBBPermultations (Scope, 1, lst) ; KillList (lst) END ; bbArrayKill END END ScopeBlockVariableAnalysis ; (* GetOp3 - *) PROCEDURE GetOp3 (quad: CARDINAL) : CARDINAL ; VAR op: QuadOperator ; op1, op2, op3: CARDINAL ; BEGIN GetQuad (quad, op, op1, op2, op3) ; RETURN op3 END GetOp3 ; (* getBBindex - return the basic block index which starts with quad. *) PROCEDURE getBBindex (quad: CARDINAL) : CARDINAL ; VAR iPtr : bbEntry ; i, high: CARDINAL ; BEGIN i := 1 ; high := Indexing.HighIndice (bbArray) ; WHILE i <= high DO iPtr := Indexing.GetIndice (bbArray, i) ; IF iPtr^.start = quad THEN RETURN iPtr^.indexBB END ; INC (i) END ; RETURN 0 END getBBindex ; (* GenerateCFG - *) PROCEDURE GenerateCFG ; VAR iPtr : bbEntry ; next, i, high: CARDINAL ; BEGIN i := 1 ; high := Indexing.HighIndice (bbArray) ; WHILE i <= high DO iPtr := Indexing.GetIndice (bbArray, i) ; IF IsKillLocalVar (iPtr^.end) OR IsReturn (iPtr^.end) THEN (* Nothing to do as we have reached the end of this scope. *) ELSE next := GetNextQuad (iPtr^.end) ; iPtr^.nextQuad := next ; iPtr^.nextBB := getBBindex (next) ; IF iPtr^.endCond THEN iPtr^.condQuad := GetOp3 (iPtr^.end) ; iPtr^.condBB := getBBindex (iPtr^.condQuad) END END ; INC (i) END END GenerateCFG ; (* NewEntry - *) PROCEDURE NewEntry () : bbEntry ; VAR bbPtr: bbEntry ; BEGIN IF bbFreeList = NIL THEN NEW (bbPtr) ELSE bbPtr := bbFreeList ; bbFreeList := bbFreeList^.next END ; RETURN bbPtr END NewEntry ; (* IsAllocate - return TRUE is sym is ALLOCATE. *) PROCEDURE IsAllocate (sym: CARDINAL) : BOOLEAN ; BEGIN RETURN IsProcedure (sym) AND (GetSymName (sym) = MakeKey('ALLOCATE')) END IsAllocate ; (* IsDeallocate - return TRUE is sym is DEALLOCATE. *) PROCEDURE IsDeallocate (sym: CARDINAL) : BOOLEAN ; BEGIN RETURN IsProcedure (sym) AND (GetSymName (sym) = MakeKey('DEALLOCATE')) END IsDeallocate ; (* DetectTrash - *) PROCEDURE DetectTrash (bbPtr: bbEntry) ; VAR i : CARDINAL ; op : QuadOperator ; op1, op2, op3: CARDINAL ; BEGIN IF bbPtr^.endCall THEN i := bbPtr^.start ; LOOP GetQuad (i, op, op1, op2, op3) ; IF (op = ParamOp) AND (op1 = 1) AND (IsAllocate (op2) OR IsDeallocate (op2)) THEN bbPtr^.trashQuad := i END ; IF i = bbPtr^.end THEN RETURN END ; i := GetNextQuad (i) END END END DetectTrash ; (* AppendEntry - *) PROCEDURE AppendEntry (Start, End: CARDINAL) ; VAR bbPtr: bbEntry ; high : CARDINAL ; BEGIN high := Indexing.HighIndice (bbArray) ; bbPtr := NewEntry () ; WITH bbPtr^ DO start := Start ; end := End ; first := high = 0 ; endCall := IsCall (End) ; endGoto := IsGoto (End) ; endCond := IsConditional (End) ; topOfLoop := IsBackReference (Start) ; trashQuad := 0 ; indexBB := high + 1 ; nextQuad := 0 ; condQuad := 0 ; nextBB := 0 ; condBB := 0 ; next := NIL END ; DetectTrash (bbPtr) ; Indexing.PutIndice (bbArray, high + 1, bbPtr) END AppendEntry ; (* DumpAlias - *) PROCEDURE DumpAlias (array: Index; aliasIndex: CARDINAL) ; VAR sa: symAlias ; BEGIN sa := Indexing.GetIndice (array, aliasIndex) ; printf2 ("keySym = %d: alias = %d\n", sa^.keySym, sa^.alias) END DumpAlias ; (* doDumpAliases - *) PROCEDURE doDumpAliases (array: Index) ; VAR i, n: CARDINAL ; BEGIN i := 1 ; n := Indexing.HighIndice (array) ; WHILE i <= n DO DumpAlias (array, i) ; INC (i) END END doDumpAliases ; (* DumpAliases - *) PROCEDURE DumpAliases ; BEGIN IF Debugging THEN printf0 ("LArray\n") ; doDumpAliases (LArray) ; printf0 ("IndirectArray\n") ; doDumpAliases (IndirectArray) END END DumpAliases ; (* newAlias - *) PROCEDURE newAlias () : symAlias ; VAR sa: symAlias ; BEGIN IF freeList = NIL THEN NEW (sa) ELSE sa := freeList ; freeList := freeList^.next END ; RETURN sa END newAlias ; (* initAlias - *) PROCEDURE initAlias (sym: CARDINAL) : symAlias ; VAR sa: symAlias ; BEGIN sa := newAlias () ; WITH sa^ DO keySym := sym ; alias := NulSym ; next := NIL END ; RETURN sa END initAlias ; (* killAlias - *) PROCEDURE killAlias (sa: symAlias) ; BEGIN sa^.next := freeList ; freeList := sa END killAlias ; (* initBlock - *) PROCEDURE initBlock ; BEGIN LArray := Indexing.InitIndex (1) ; IndirectArray := Indexing.InitIndex (1) ; InitList (ignoreList) END initBlock ; (* killBlock - *) PROCEDURE killBlock ; BEGIN doKillBlock (LArray) ; doKillBlock (IndirectArray) ; KillList (ignoreList) END killBlock ; PROCEDURE doKillBlock (VAR array: Index) ; VAR i, n: CARDINAL ; BEGIN i := 1 ; n := Indexing.HighIndice (array) ; WHILE i <= n DO killAlias (Indexing.GetIndice (array, i)) ; INC (i) END ; array := Indexing.KillIndex (array) END doKillBlock ; (* addAlias - *) PROCEDURE addAlias (array: Index; sym: CARDINAL; aliased: CARDINAL) ; VAR i, n: CARDINAL ; sa : symAlias ; BEGIN i := 1 ; n := Indexing.HighIndice (array) ; WHILE i <= n DO sa := Indexing.GetIndice (array, i) ; IF sa^.keySym = sym THEN sa^.alias := aliased ; RETURN END ; INC (i) END ; sa := initAlias (sym) ; Indexing.IncludeIndiceIntoIndex (array, sa) ; sa^.alias := aliased END addAlias ; (* lookupAlias - *) PROCEDURE lookupAlias (array: Index; sym: CARDINAL) : symAlias ; VAR i, n: CARDINAL ; sa : symAlias ; BEGIN i := 1 ; n := Indexing.HighIndice (array) ; WHILE i <= n DO sa := Indexing.GetIndice (array, i) ; IF sa^.keySym = sym THEN RETURN sa END ; INC (i) END ; RETURN NIL END lookupAlias ; (* doGetAlias - *) PROCEDURE doGetAlias (array: Index; sym: CARDINAL) : CARDINAL ; VAR sa: symAlias ; BEGIN sa := lookupAlias (array, sym) ; IF (sa # NIL) AND (sa^.alias # NulSym) THEN RETURN sa^.alias END ; RETURN NulSym END doGetAlias ; (* getLAlias - attempts to looks up an alias which is not a temporary variable. *) PROCEDURE getLAlias (sym: CARDINAL) : CARDINAL ; VAR type, nsym: CARDINAL ; BEGIN nsym := sym ; REPEAT sym := nsym ; type := GetSType (sym) ; IF (IsTemporary (sym) AND (GetMode (sym) = LeftValue)) OR ((type # NulSym) AND IsReallyPointer (type)) THEN nsym := doGetAlias (LArray, sym) ELSE RETURN sym END UNTIL nsym = NulSym ; RETURN sym END getLAlias ; (* SetupLAlias - *) PROCEDURE SetupLAlias (des, exp: CARDINAL) ; BEGIN IF (exp = Nil) OR (IsVar (exp) AND ((GetMode (des) = LeftValue) OR IsReallyPointer (GetSType (des)))) THEN addAlias (LArray, des, exp) ; DumpAliases END END SetupLAlias ; (* SetupIndr - *) PROCEDURE SetupIndr (ptr, content: CARDINAL) ; BEGIN addAlias (IndirectArray, ptr, content) ; END SetupIndr ; (* getContent - attempts to return the content pointed to by ptr. sym is the original symbol and ptr will be the equivalent lvalue. *) PROCEDURE getContent (ptr: CARDINAL; sym: CARDINAL; tok: CARDINAL) : CARDINAL ; BEGIN IF ptr = Nil THEN MetaErrorT1 (tok, "attempting to dereference {%1Wad} which will be a {%kNIL} pointer", sym) ; RETURN NulSym ELSE RETURN doGetAlias (IndirectArray, ptr) END END getContent ; (* init - *) PROCEDURE init ; BEGIN freeList := NIL ; bbFreeList := NIL ; InitList (errorList) END init ; BEGIN init END M2SymInit.