| (* M2Optimize.mod removes redundant quadruples. |
| |
| 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 M2Optimize ; |
| |
| (* |
| Title : M2Optimize |
| Author : Gaius Mulley |
| System : UNIX (GNU Modula-2) |
| Date : Sat Aug 14 15:07:47 1999 |
| Description: removes redundant quadruples, redundant GotoOps, redundant procedures. |
| *) |
| |
| FROM M2Debug IMPORT Assert ; |
| |
| FROM NameKey IMPORT Name, WriteKey, MakeKey, GetKey ; |
| FROM StrIO IMPORT WriteString, WriteLn ; |
| FROM NumberIO IMPORT WriteCard ; |
| |
| FROM M2Error IMPORT InternalError ; |
| FROM M2Batch IMPORT GetModuleNo ; |
| FROM M2Quiet IMPORT qprintf1 ; |
| FROM M2Scope IMPORT ScopeBlock, InitScopeBlock, KillScopeBlock, |
| ForeachScopeBlockDo2, ForeachScopeBlockDo3 ; |
| |
| FROM SymbolTable IMPORT GetSymName, |
| GetProcedureQuads, GetModuleQuads, |
| GetModule, GetNthProcedure, |
| GetSubrange, GetModuleScope, |
| PutProcedureReachable, IsProcedureReachable, |
| PutProcedureStartQuad, PutProcedureEndQuad, |
| PutProcedureScopeQuad, |
| PutNeedSavePriority, |
| IsProcedure, GetPriority, |
| GetDeclaredMod, GetFirstUsed, |
| GetType, |
| IsExportQualified, IsExportUnQualified, IsExported, |
| ForeachProcedureDo, ForeachInnerModuleDo, |
| IsModuleWithinProcedure, |
| NulSym ; |
| |
| FROM M2Quads IMPORT QuadOperator, GetQuad, GetFirstQuad, GetNextQuad, |
| PutQuad, SubQuad, Opposite, IsReferenced, |
| GetRealQuad, GetQuadOtok, PutQuadOtok ; |
| |
| (* |
| FoldBranches - folds unneccessary branches in the list of quadruples. |
| It searches for the following patterns: |
| |
| [x] GotoOp _ _ y GotoOp _ _ z |
| ... ... |
| [y] GotoOp _ _ z "deleted" |
| |
| WHERE ... may contain 0..n Pseudo Quads |
| |
| |
| OR |
| |
| |
| [x] IfREL _ _ z If NOT REL _ _ a |
| ... ... |
| [y] Goto _ _ a "deleted" |
| ... ... |
| [z] |
| |
| |
| WHERE ... may contain 0..n Pseudo Quads |
| but in this case they must not be a |
| target of any other quad. |
| *) |
| |
| PROCEDURE FoldBranches (start, end: CARDINAL) ; |
| VAR |
| Folded : BOOLEAN ; |
| i, j, |
| Right : CARDINAL ; |
| Operator : QuadOperator ; |
| Operand1, |
| Operand2, |
| Operand3 : CARDINAL ; |
| BEGIN |
| REPEAT |
| i := start ; |
| Folded := FALSE ; |
| WHILE (i<=end) AND (i#0) DO |
| j := GetNextQuad(i) ; |
| IF (j>end) OR (j=0) |
| THEN |
| RETURN |
| END ; |
| Right := GetRealQuad(j) ; |
| IF Right=0 |
| THEN |
| RETURN |
| END ; |
| GetQuad(i, Operator, Operand1, Operand2, Operand3) ; |
| CASE Operator OF |
| |
| GotoOp : Folded := ReduceGoto (i, Operand3, |
| Right, Folded) | |
| IfInOp, IfNotInOp, |
| IfNotEquOp, IfEquOp, |
| IfLessEquOp, IfGreEquOp, |
| IfGreOp, IfLessOp : Folded := ReduceBranch (Operator, i, |
| Operand1, Operand2, Operand3, |
| Right, Folded) |
| |
| ELSE |
| END ; |
| i := Right |
| END |
| UNTIL NOT Folded |
| END FoldBranches ; |
| |
| |
| (* |
| ReduceBranch - searches for the following pattern: |
| |
| [x] IfREL _ _ z If NOT REL _ _ a |
| ... ... |
| [y] Goto _ _ a "deleted" |
| ... ... |
| [z] |
| |
| |
| WHERE ... may contain 0..n Pseudo Quads |
| but in this case they must not be a |
| target of any other quad. |
| |
| *) |
| |
| PROCEDURE ReduceBranch (Operator: QuadOperator; |
| CurrentQuad, |
| CurrentOperand1, CurrentOperand2, |
| CurrentOperand3: CARDINAL; |
| VAR NextQuad: CARDINAL; |
| Folded: BOOLEAN) : BOOLEAN ; |
| VAR |
| constExpr, |
| overflowChecking: BOOLEAN ; |
| OpNext : QuadOperator ; |
| tok, |
| NextPlusOne, |
| Op1Next, |
| Op2Next, |
| Op3Next, |
| op1tok, |
| op2tok, |
| op3tok, |
| From, To : CARDINAL ; |
| BEGIN |
| (* If op NextQuad+1 *) |
| (* Goto x *) |
| |
| IF NextQuad#0 |
| THEN |
| IF (GetNextQuad (CurrentQuad) = CurrentOperand3) OR |
| (GetRealQuad (GetNextQuad (CurrentQuad)) = CurrentOperand3) |
| THEN |
| SubQuad (CurrentQuad) ; |
| Folded := TRUE |
| ELSE |
| From := GetNextQuad (CurrentQuad) ; (* start after CurrentQuad *) |
| To := NextQuad ; |
| CurrentOperand3 := GetRealQuad (CurrentOperand3) ; |
| |
| NextPlusOne := GetRealQuad (GetNextQuad (NextQuad)) ; |
| GetQuad (NextQuad, OpNext, Op1Next, Op2Next, Op3Next) ; |
| IF (OpNext = GotoOp) AND (NextPlusOne = CurrentOperand3) AND |
| IsBasicBlock (From, To) |
| THEN |
| GetQuadOtok (CurrentQuad, tok, Operator, |
| CurrentOperand1, CurrentOperand2, CurrentOperand3, |
| overflowChecking, constExpr, op1tok, op2tok, op3tok) ; |
| SubQuad (NextQuad) ; |
| PutQuadOtok (CurrentQuad, tok, Opposite (Operator), |
| CurrentOperand1, CurrentOperand2, Op3Next, |
| overflowChecking, constExpr, |
| op1tok, op2tok, op3tok) ; |
| NextQuad := NextPlusOne ; |
| Folded := TRUE |
| END |
| END ; |
| IF FoldMultipleGoto (CurrentQuad) |
| THEN |
| Folded := TRUE |
| END |
| END ; |
| RETURN Folded |
| END ReduceBranch ; |
| |
| |
| (* |
| IsBasicBlock - returns TRUE if no other quadruple jumps inbetween |
| the range From..To. |
| It assumes that there are no jumps in the quadruples |
| From..To. |
| *) |
| |
| PROCEDURE IsBasicBlock (From, To: CARDINAL) : BOOLEAN ; |
| BEGIN |
| WHILE From # To DO |
| IF IsReferenced (From) |
| THEN |
| RETURN FALSE |
| ELSE |
| IF From > To |
| THEN |
| InternalError ('assert failed From should never be larger than To') |
| END ; |
| From := GetNextQuad (From) |
| END |
| END ; |
| RETURN TRUE |
| END IsBasicBlock ; |
| |
| |
| (* |
| ReduceGoto - searches for the following patterns: |
| |
| [x] GotoOp _ _ y GotoOp _ _ z |
| ... ... |
| [y] GotoOp _ _ z "deleted" |
| |
| |
| *) |
| |
| PROCEDURE ReduceGoto (CurrentQuad, CurrentOperand3, NextQuad: CARDINAL; |
| Folded: BOOLEAN) : BOOLEAN ; |
| BEGIN |
| CurrentOperand3 := GetRealQuad (CurrentOperand3) ; |
| (* IF next quad is a GotoOp *) |
| IF CurrentOperand3 = NextQuad |
| THEN |
| SubQuad (CurrentQuad) ; |
| Folded := TRUE |
| ELSE |
| (* Does Goto point to another Goto ? *) |
| IF FoldMultipleGoto (CurrentQuad) |
| THEN |
| Folded := TRUE |
| END |
| END ; |
| RETURN Folded |
| END ReduceGoto ; |
| |
| |
| (* |
| FoldMultipleGoto - takes a QuadNo and if it jumps to another GotoOp |
| then it takes the later target as a replacement |
| for its own. |
| |
| NOTE it does not remove any quadruples. |
| *) |
| |
| PROCEDURE FoldMultipleGoto (QuadNo: CARDINAL) : BOOLEAN ; |
| VAR |
| Operator, |
| Op : QuadOperator ; |
| Op1, Op2, |
| Op3, |
| Operand1, |
| Operand2, |
| Operand3: CARDINAL ; |
| BEGIN |
| GetQuad (QuadNo, Operator, Operand1, Operand2, Operand3) ; |
| Operand3 := GetRealQuad (Operand3) ; (* skip pseudo quadruples *) |
| GetQuad (Operand3, Op, Op1, Op2, Op3) ; |
| IF Op = GotoOp |
| THEN |
| PutQuad (QuadNo, Operator, Operand1, Operand2, Op3) ; |
| (* Dont want success to be returned if in fact the Goto *) |
| (* line number has not changed... otherwise we loop *) |
| (* forever. *) |
| RETURN Op3 # Operand3 |
| ELSE |
| RETURN FALSE |
| END |
| END FoldMultipleGoto ; |
| |
| |
| (* |
| CheckNeedSavePriority - |
| *) |
| |
| PROCEDURE CheckNeedSavePriority (sym: CARDINAL) ; |
| BEGIN |
| IF IsProcedure(sym) AND (GetPriority(GetModuleScope(sym))#NulSym) |
| THEN |
| PutNeedSavePriority(sym) |
| END |
| END CheckNeedSavePriority ; |
| |
| |
| (* |
| CheckExportedReachable - checks to see whether procedure, sym, was |
| exported and if so it calls RemoveProcedures. |
| *) |
| |
| PROCEDURE CheckExportedReachable (sym: CARDINAL) ; |
| BEGIN |
| IF IsExported(GetModuleScope(sym), sym) |
| THEN |
| RemoveProcedures(sym) ; |
| CheckNeedSavePriority(sym) |
| END |
| END CheckExportedReachable ; |
| |
| |
| (* |
| RemoveProcedures - removes any procedures that are never referenced |
| by the quadruples. |
| *) |
| |
| PROCEDURE RemoveProcedures (scope: CARDINAL) ; |
| VAR |
| sb: ScopeBlock ; |
| BEGIN |
| sb := InitScopeBlock(scope) ; |
| IF IsProcedure(scope) |
| THEN |
| PutProcedureReachable(scope) ; |
| ForeachScopeBlockDo2 (sb, KnownReachable) |
| ELSIF IsModuleWithinProcedure(scope) |
| THEN |
| ForeachScopeBlockDo2 (sb, KnownReachable) ; |
| ForeachProcedureDo(scope, CheckExportedReachable) |
| ELSE |
| ForeachScopeBlockDo2 (sb, KnownReachable) ; |
| ForeachProcedureDo(scope, CheckExportedReachable) |
| END ; |
| ForeachInnerModuleDo(scope, RemoveProcedures) ; |
| KillScopeBlock(sb) ; |
| (* DeleteUnReachableProcedures *) |
| END RemoveProcedures ; |
| |
| |
| PROCEDURE KnownReachable (Start, End: CARDINAL) ; |
| VAR |
| Op : QuadOperator ; |
| Op1, Op2, Op3: CARDINAL ; |
| BEGIN |
| IF Start#0 |
| THEN |
| REPEAT |
| GetQuad (Start, Op, Op1, Op2, Op3) ; |
| CASE Op OF |
| |
| CallOp : KnownReach (Op3) | |
| AddrOp, |
| ParamOp, |
| XIndrOp, |
| BecomesOp: KnownReach (Op3) ; |
| CheckNeedSavePriority (Op3) |
| |
| ELSE |
| END ; |
| Start := GetNextQuad (Start) |
| UNTIL (Start > End) OR (Start = 0) |
| END |
| END KnownReachable ; |
| |
| |
| PROCEDURE KnownReach (sym: CARDINAL) ; |
| BEGIN |
| IF IsProcedure (sym) AND (NOT IsProcedureReachable (sym)) |
| THEN |
| RemoveProcedures (sym) |
| END |
| END KnownReach ; |
| |
| |
| (* |
| DeleteUnReachableProcedures - Deletes all procedures that are unreachable. |
| *) |
| |
| (* |
| PROCEDURE DeleteUnReachableProcedures ; |
| VAR |
| ProcName: Name ; |
| n, m, |
| Scope, |
| Start, |
| End, |
| Module, |
| Proc : CARDINAL ; |
| BEGIN |
| m := 1 ; |
| REPEAT |
| Module := GetModuleNo(m) ; |
| IF Module#NulSym |
| THEN |
| n := 1 ; |
| Proc := GetNthProcedure(Module, n) ; |
| WHILE Proc#NulSym DO |
| IF IsProcedureReachable(Proc) OR |
| IsExportQualified(Proc) OR IsExportUnQualified(Proc) |
| THEN |
| (* is reachable - do not delete it *) |
| ELSE |
| ProcName := GetSymName(Proc) ; |
| qprintf1('[%a]\n', ProcName) ; |
| |
| GetProcedureQuads(Proc, Scope, Start, End) ; |
| IF Start#0 |
| THEN |
| Delete(Scope, End) ; |
| (* No Longer any Quads for this Procedure *) |
| PutProcedureScopeQuad(Proc, 0) ; |
| PutProcedureStartQuad(Proc, 0) ; |
| PutProcedureEndQuad(Proc, 0) |
| END |
| END ; |
| INC(n) ; |
| Proc := GetNthProcedure(Module, n) |
| END ; |
| INC(m) |
| END |
| UNTIL Module=NulSym |
| END DeleteUnReachableProcedures ; |
| |
| |
| (* |
| Delete - deletes all quadruples from Start..End |
| or the end of the procedure. |
| *) |
| |
| PROCEDURE Delete (Start, End: CARDINAL) ; |
| VAR |
| Last, |
| i : CARDINAL ; |
| Op : QuadOperator ; |
| Op1, |
| Op2, |
| Op3 : CARDINAL ; |
| BEGIN |
| Last := GetNextQuad(End) ; |
| WHILE (GetFirstQuad()#0) AND (Start#0) AND (Last#Start) DO |
| GetQuad(Start, Op, Op1, Op2, Op3) ; |
| IF Op=DummyOp |
| THEN |
| (* Start has already been deleted - try next quad *) |
| INC(Start) |
| ELSIF Op=ReturnOp |
| THEN |
| (* Found end of procedure therefore just delete and exit *) |
| (* WriteString('Deleting') ; WriteCard(Start, 6) ; WriteLn ; *) |
| SubQuad(Start) ; |
| Start := Last |
| ELSE |
| (* Following the list of quadruples to the End *) |
| i := GetNextQuad(Start) ; |
| (* WriteString('Deleting') ; WriteCard(Start, 6) ; WriteLn ; *) |
| SubQuad(Start) ; |
| Start := i |
| END |
| END |
| END Delete ; |
| *) |
| |
| |
| (* |
| DisplayReachable - Displays the data structures surrounding Reachablity. |
| *) |
| |
| PROCEDURE DisplayReachable ; |
| VAR |
| n, m, |
| Scope, |
| StartInit, |
| EndInit, |
| StartFinish, |
| EndFinish, |
| Module, |
| Proc : CARDINAL ; |
| BEGIN |
| m := 1 ; |
| REPEAT |
| Module := GetModuleNo(m) ; |
| IF Module#NulSym |
| THEN |
| WriteString('Module') ; WriteCard(m, 3) ; WriteKey(GetSymName(Module)) ; |
| GetModuleQuads(Module, StartInit, EndInit, StartFinish, EndFinish) ; |
| WriteString(' Reachable initialization') ; |
| WriteCard(StartInit, 6) ; WriteCard(EndInit, 6) ; WriteLn ; |
| WriteString('Module') ; WriteCard(m, 3) ; WriteKey(GetSymName(Module)) ; |
| GetModuleQuads(Module, StartInit, EndInit, StartFinish, EndFinish) ; |
| WriteString(' Reachable finalization') ; |
| WriteCard(StartFinish, 6) ; WriteCard(EndFinish, 6) ; WriteLn ; |
| n := 1 ; |
| Proc := GetNthProcedure(Module, n) ; |
| WHILE Proc#NulSym DO |
| WriteString('Procedure ') ; WriteKey(GetSymName(Proc)) ; |
| GetProcedureQuads(Proc, Scope, StartInit, EndInit) ; |
| WriteString(' Quads: ') ; WriteCard(StartInit, 6) ; WriteCard(EndInit, 6) ; |
| IF NOT IsProcedureReachable(Proc) |
| THEN |
| WriteString(' UN reachable') |
| ELSE |
| WriteString(' IS reachable') |
| END ; |
| WriteLn ; |
| INC(n) ; |
| Proc := GetNthProcedure(Module, n) |
| END ; |
| INC(m) |
| END |
| UNTIL Module=NulSym |
| END DisplayReachable ; |
| |
| |
| END M2Optimize. |