blob: 2740395cf86ddff071997ea9763f9512f5cad280 [file] [log] [blame]
(* M2SymInit.mod records initialization state for variables.
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 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,
GetBasicBlockStart, GetBasicBlockEnd ;
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,
ProcedureKind, GetNthParam, NoOfParam,
GetNth, IsRecordField, IsSet, IsArray, IsProcedure,
GetVarScope, IsVarAParam, IsComponent, GetMode,
VarCheckReadInit, VarInitState, PutVarInitialized,
PutVarFieldInitialized, GetVarFieldInitialized,
IsConst, IsConstString, NoOfParamAny, IsVarParamAny,
ForeachLocalSymDo, ForeachParamSymDo,
IsTemporary, ModeOfAddr,
IsReallyPointer, IsUnbounded,
IsVarient, IsFieldVarient, GetVarient,
IsVarArrayRef, GetSymName,
IsType, IsPointer, IsTuple,
GetParameterShadowVar, IsParameter, GetLType,
GetParameterHeapVar, GetVarDeclTok ;
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
IF desc^.rec.fieldDesc = NIL
THEN
printf (" record field descriptor has not been initialized yet\n") ;
ELSE
i := 1 ;
n := Indexing.HighIndice (desc^.rec.fieldDesc) ;
WHILE i <= n DO
PrintSymInit (Indexing.GetIndice (desc^.rec.fieldDesc, i)) ;
INC (i)
END
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 ;
(* An unknown symbol will have no type. *)
IF desc^.type = NulSym
THEN
desc^.kind := scalar ;
desc^.initialized := TRUE (* For now we don't attempt to handle array types. *)
ELSE
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
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
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
IF SetFieldInitializedNo (fdesc, fieldlist, level + 1)
THEN
END ;
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: 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 ;
constExpr, overflowChecking : BOOLEAN ;
s : String ;
BEGIN
GetQuadOtok (quad, qtok, op, op1, op2, op3,
overflowChecking, constExpr,
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 (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 (tok: CARDINAL;
sym: CARDINAL;
canDereference, warning: BOOLEAN;
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 (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 (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 (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 (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 (op1tok, op1,
op2tok, op2,
op3tok, op3: CARDINAL; warning: BOOLEAN;
i: CARDINAL) ;
BEGIN
CheckDeferredRecordAccess (op2tok, op2, FALSE, warning, i) ;
CheckDeferredRecordAccess (op3tok, op3, FALSE, warning, i) ;
SetVarInitialized (op1, FALSE, op1tok)
END CheckBinary ;
(*
CheckUnary -
*)
PROCEDURE CheckUnary (lhstok, lhs,
rhstok, rhs: CARDINAL; warning: BOOLEAN;
i: CARDINAL) ;
BEGIN
CheckDeferredRecordAccess (rhstok, rhs, FALSE, warning, i) ;
SetVarInitialized (lhs, FALSE, lhstok)
END CheckUnary ;
(*
CheckXIndr -
*)
PROCEDURE CheckXIndr (lhstok, lhs, type,
rhstok, rhs: CARDINAL; warning: BOOLEAN;
i: CARDINAL) ;
VAR
lst : List ;
content: CARDINAL ;
BEGIN
CheckDeferredRecordAccess (rhstok, rhs, FALSE, warning, i) ;
CheckDeferredRecordAccess (lhstok, lhs, FALSE, warning, 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 (lhstok, lhs, rhstok, rhs: CARDINAL;
warning: BOOLEAN;
i: CARDINAL) ;
VAR
content: CARDINAL ;
BEGIN
CheckDeferredRecordAccess (rhstok, rhs, FALSE, warning, i) ;
content := getContent (getLAlias (rhs), rhs, rhstok) ;
IF content = NulSym
THEN
IncludeItemIntoList (ignoreList, lhs)
ELSE
CheckDeferredRecordAccess (rhstok, content, TRUE, warning, i) ;
SetVarInitialized (lhs, VarCheckReadInit (content, RightValue), lhstok) ;
IF IsReallyPointer (content)
THEN
SetupLAlias (lhs, content)
END
END
END CheckIndrX ;
(*
CheckRecordField -
*)
PROCEDURE CheckRecordField (op1: CARDINAL) ;
BEGIN
PutVarInitialized (op1, LeftValue)
END CheckRecordField ;
(*
CheckLastForIterator -
*)
PROCEDURE CheckLastForIterator (op1tok: CARDINAL; op1: CARDINAL;
op2tok: CARDINAL; op2: CARDINAL;
warning: BOOLEAN; i: CARDINAL) ;
BEGIN
SetVarInitialized (op1, FALSE, op1tok) ;
Assert (IsTuple (op2)) ;
CheckDeferredRecordAccess (op2tok, GetNth (op2, 1), FALSE, warning, i) ;
CheckDeferredRecordAccess (op2tok, GetNth (op2, 2), FALSE, warning, i) ;
END CheckLastForIterator ;
(*
CheckBecomes -
*)
PROCEDURE CheckBecomes (destok, des, exprtok, expr: CARDINAL;
warning: BOOLEAN; i: CARDINAL) ;
VAR
lvalue: BOOLEAN ;
lst : List ;
vsym : CARDINAL ;
BEGIN
CheckDeferredRecordAccess (exprtok, expr, FALSE, warning, 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 (op1tok, op1, op2tok, op2: CARDINAL;
warning: BOOLEAN; i: CARDINAL) ;
BEGIN
CheckDeferredRecordAccess (op1tok, op1, FALSE, warning, i) ;
CheckDeferredRecordAccess (op2tok, op2, FALSE, warning, i)
END CheckComparison ;
(*
CheckAddr -
*)
PROCEDURE CheckAddr (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; i: CARDINAL) : BOOLEAN ;
VAR
op : QuadOperator ;
op1, op2, op3 : CARDINAL ;
op1tok, op2tok, op3tok, qtok: CARDINAL ;
constExpr, 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, constExpr,
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 (op1tok, op1, op2tok, op2, warning, i) |
LastForIteratorOp : CheckLastForIterator (op1tok, op1, op2tok, op2,
warning, i) ;
Assert (IsConst (op3)) |
TryOp,
ReturnOp,
CallOp,
KillLocalVarOp,
RetryOp,
GotoOp : RETURN TRUE | (* End of basic block. *)
(* Variable references. *)
InclOp,
ExclOp : CheckDeferredRecordAccess (op1tok, op1, FALSE, warning, i) ;
CheckDeferredRecordAccess (op1tok, op1, TRUE, warning, i) ;
CheckDeferredRecordAccess (op3tok, op3, FALSE, warning, i) |
NegateOp : CheckUnary (op1tok, op1, op3tok, op3, warning, i) |
BecomesOp : CheckBecomes (op1tok, op1, op3tok, op3, warning, i) |
UnboundedOp,
FunctValueOp,
StandardFunctionOp,
HighOp,
SizeOp : SetVarInitialized (op1, FALSE, op1tok) |
AddrOp : CheckAddr (op1tok, op1, op3tok, op3) |
ReturnValueOp : SetVarInitialized (op1, FALSE, op1tok) |
NewLocalVarOp : SetParameterVariablesInitialized (op3) |
ParamOp : CheckDeferredRecordAccess (op2tok, op2, FALSE, warning, i) ;
CheckDeferredRecordAccess (op3tok, op3, FALSE, warning, i) ;
IF (op1 > 0) AND (op1 <= NoOfParamAny (op2)) AND
IsVarParamAny (op2, op1)
THEN
SetVarInitialized (op3, TRUE, op3tok)
END |
ArrayOp : CheckDeferredRecordAccess (op3tok, op3, FALSE, warning, i) ;
SetVarInitialized (op1, TRUE, op1tok) |
RecordFieldOp : CheckRecordField (op1) |
LogicalShiftOp,
LogicalRotateOp,
LogicalOrOp,
LogicalAndOp,
LogicalXorOp,
LogicalDiffOp,
CoerceOp,
ConvertOp,
CastOp,
AddOp,
ArithAddOp,
SubOp,
MultOp,
DivM2Op,
ModM2Op,
ModFloorOp,
DivCeilOp,
ModCeilOp,
DivFloorOp,
ModTruncOp,
DivTruncOp : CheckBinary (op1tok, op1, op2tok, op2, op3tok, op3, warning, i) |
XIndrOp : CheckXIndr (op1tok, op1, op2, op3tok, op3, warning, i) |
IndrXOp : CheckIndrX (op1tok, op1, op3tok, op3, warning, i) |
SaveExceptionOp : SetVarInitialized (op1, FALSE, op1tok) |
RestoreExceptionOp: CheckDeferredRecordAccess (op1tok, op1, FALSE, warning, 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. *)
StringConvertCnulOp,
StringConvertM2nulOp,
StringLengthOp,
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 ;
(*
SetParameterVariablesInitialized - sets all shadow variables for parameters as
initialized.
*)
PROCEDURE SetParameterVariablesInitialized (procSym: CARDINAL) ;
BEGIN
ForeachLocalSymDo (procSym, SetVarUninitialized) ;
ForeachParamSymDo (procSym, SetVarLRInitialized) ;
END SetParameterVariablesInitialized ;
(*
FilterCheckReadBeforeInitQuad -
*)
PROCEDURE FilterCheckReadBeforeInitQuad (procSym: CARDINAL; start: CARDINAL;
warning: BOOLEAN;
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, i)
END ;
RETURN FALSE
END FilterCheckReadBeforeInitQuad ;
(*
CheckReadBeforeInitFirstBasicBlock -
*)
PROCEDURE CheckReadBeforeInitFirstBasicBlock (procSym: CARDINAL;
start, end: CARDINAL;
warning: BOOLEAN;
i: CARDINAL) ;
BEGIN
LOOP
IF FilterCheckReadBeforeInitQuad (procSym, start, warning, 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 (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) ;
printf2 (" lst[%d] -> %d", listindex, arrayindex) ;
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 ;
constExpr, overflowChecking : BOOLEAN ;
heapValue, ptrToHeap : CARDINAL ;
BEGIN
IF trashQuad # 0
THEN
GetQuadOtok (trashQuad, qtok, op, op1, proc, param,
overflowChecking, constExpr,
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 (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, 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 (bb: BasicBlock) ;
VAR
bbPtr: bbEntry ;
high : CARDINAL ;
BEGIN
high := Indexing.HighIndice (bbArray) ;
bbPtr := NewEntry () ;
WITH bbPtr^ DO
start := GetBasicBlockStart (bb) ;
end := GetBasicBlockEnd (bb) ;
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.