From 7cd141f28eea622b117bd6d2279ebf1f0274842b Mon Sep 17 00:00:00 2001 From: jzigman Date: Mon, 1 Jan 2007 22:36:41 +0000 Subject: [PATCH] Initial revision --- lgpl.license.txt | 504 +++ src/edu/purdue/cs/bloat/Makefile | 65 + src/edu/purdue/cs/bloat/cfg/Block.java | 442 ++ .../cs/bloat/cfg/DominanceFrontier.java | 172 + .../purdue/cs/bloat/cfg/DominatorTree.java | 399 ++ src/edu/purdue/cs/bloat/cfg/FlowGraph.java | 3688 ++++++++++++++++ src/edu/purdue/cs/bloat/cfg/Handler.java | 97 + src/edu/purdue/cs/bloat/cfg/Makefile | 31 + .../purdue/cs/bloat/cfg/ReplaceTarget.java | 194 + src/edu/purdue/cs/bloat/cfg/Subroutine.java | 296 ++ src/edu/purdue/cs/bloat/cfg/VerifyCFG.java | 445 ++ src/edu/purdue/cs/bloat/cfg/package.html | 14 + src/edu/purdue/cs/bloat/class.mk | 47 + .../cs/bloat/codegen/CodeGenerator.java | 2735 ++++++++++++ src/edu/purdue/cs/bloat/codegen/Liveness.java | 823 ++++ src/edu/purdue/cs/bloat/codegen/Makefile | 27 + .../cs/bloat/codegen/RegisterAllocator.java | 752 ++++ src/edu/purdue/cs/bloat/codegen/package.html | 9 + .../purdue/cs/bloat/context/BloatContext.java | 295 ++ src/edu/purdue/cs/bloat/context/Makefile | 26 + .../bloat/context/PersistentBloatContext.java | 483 ++ src/edu/purdue/cs/bloat/context/package.html | 17 + .../cs/bloat/diva/InductionVarAnalyzer.java | 537 +++ src/edu/purdue/cs/bloat/diva/Makefile | 25 + src/edu/purdue/cs/bloat/diva/package.html | 8 + .../cs/bloat/editor/ClassHierarchy.java | 1281 ++++++ .../purdue/cs/bloat/editor/EditorContext.java | 179 + .../cs/bloat/editor/InstructionAdapter.java | 480 ++ .../cs/bloat/editor/InstructionVisitor.java | 70 + src/edu/purdue/cs/bloat/editor/Makefile | 31 + src/edu/purdue/cs/bloat/editor/MemberRef.java | 143 + src/edu/purdue/cs/bloat/editor/MethodRef.java | 117 + .../purdue/cs/bloat/editor/NameAndType.java | 112 + src/edu/purdue/cs/bloat/editor/package.html | 10 + src/edu/purdue/cs/bloat/inline/CallGraph.java | 853 ++++ src/edu/purdue/cs/bloat/inline/Inline.java | 730 ++++ .../purdue/cs/bloat/inline/InlineContext.java | 122 + .../purdue/cs/bloat/inline/InlineStats.java | 152 + src/edu/purdue/cs/bloat/inline/Makefile | 30 + .../cs/bloat/inline/StackHeightCounter.java | 246 ++ src/edu/purdue/cs/bloat/inline/package.html | 19 + src/edu/purdue/cs/bloat/optimize/CodeGen.java | 126 + .../optimize/ConstantPoolSizePrinter.java | 70 + src/edu/purdue/cs/bloat/optimize/Main.java | 921 ++++ src/edu/purdue/cs/bloat/optimize/Makefile | 31 + .../cs/bloat/optimize/MethodBloater.java | 243 ++ .../purdue/cs/bloat/optimize/MethodState.java | 140 + .../cs/bloat/optimize/Optimization.java | 90 + src/edu/purdue/cs/bloat/optimize/package.html | 7 + .../bloat/reflect/ClassFormatException.java | 50 + src/edu/purdue/cs/bloat/reflect/Makefile | 25 + src/edu/purdue/cs/bloat/reflect/package.html | 14 + .../purdue/cs/bloat/ssa/ComponentVisitor.java | 52 + src/edu/purdue/cs/bloat/ssa/Makefile | 29 + .../purdue/cs/bloat/ssa/PhiReturnStmt.java | 162 + src/edu/purdue/cs/bloat/ssa/SSA.java | 594 +++ .../cs/bloat/ssa/SSAConstructionInfo.java | 296 ++ src/edu/purdue/cs/bloat/ssa/SSAGraph.java | 731 ++++ src/edu/purdue/cs/bloat/ssa/package.html | 12 + src/edu/purdue/cs/bloat/tbaa/Makefile | 26 + src/edu/purdue/cs/bloat/tbaa/TBAA.java | 296 ++ .../purdue/cs/bloat/tbaa/TypeInference.java | 829 ++++ src/edu/purdue/cs/bloat/tbaa/package.html | 12 + .../purdue/cs/bloat/tests/BloatTester1.java | 19 + .../purdue/cs/bloat/tests/BloatTester2.java | 23 + .../purdue/cs/bloat/tests/BloatTester3.java | 25 + src/edu/purdue/cs/bloat/tests/Makefile | 28 + .../cs/bloat/trans/DeadCodeElimination.java | 712 +++ .../cs/bloat/trans/ExprPropagation.java | 301 ++ src/edu/purdue/cs/bloat/trans/Makefile | 36 + .../purdue/cs/bloat/trans/NodeComparator.java | 594 +++ src/edu/purdue/cs/bloat/trans/Peephole.java | 978 +++++ src/edu/purdue/cs/bloat/trans/SSAPRE.java | 3882 +++++++++++++++++ .../cs/bloat/trans/SideEffectChecker.java | 359 ++ src/edu/purdue/cs/bloat/trans/StackOpt.java | 327 ++ .../cs/bloat/trans/StackOptimization.java | 76 + .../purdue/cs/bloat/trans/ValueFolder.java | 2014 +++++++++ .../purdue/cs/bloat/trans/ValueFolding.java | 306 ++ .../purdue/cs/bloat/trans/ValueNumbering.java | 536 +++ src/edu/purdue/cs/bloat/trans/package.html | 45 + .../cs/bloat/tree/AddressStoreStmt.java | 82 + src/edu/purdue/cs/bloat/tree/ArithExpr.java | 146 + .../purdue/cs/bloat/tree/ArrayLengthExpr.java | 95 + .../purdue/cs/bloat/tree/ArrayRefExpr.java | 116 + .../purdue/cs/bloat/tree/AscendVisitor.java | 454 ++ src/edu/purdue/cs/bloat/tree/Assign.java | 56 + src/edu/purdue/cs/bloat/tree/CallExpr.java | 89 + .../purdue/cs/bloat/tree/CallMethodExpr.java | 141 + .../purdue/cs/bloat/tree/CallStaticExpr.java | 106 + src/edu/purdue/cs/bloat/tree/CastExpr.java | 116 + src/edu/purdue/cs/bloat/tree/CatchExpr.java | 104 + src/edu/purdue/cs/bloat/tree/CheckExpr.java | 96 + src/edu/purdue/cs/bloat/tree/CondExpr.java | 60 + .../purdue/cs/bloat/tree/ConstantExpr.java | 127 + src/edu/purdue/cs/bloat/tree/DefExpr.java | 136 + .../purdue/cs/bloat/tree/DefInformation.java | 60 + .../purdue/cs/bloat/tree/DescendVisitor.java | 452 ++ .../cs/bloat/tree/EliminationInformation.java | 65 + src/edu/purdue/cs/bloat/tree/Expr.java | 246 ++ src/edu/purdue/cs/bloat/tree/ExprStmt.java | 76 + src/edu/purdue/cs/bloat/tree/FieldExpr.java | 109 + src/edu/purdue/cs/bloat/tree/GotoStmt.java | 78 + src/edu/purdue/cs/bloat/tree/IfCmpStmt.java | 102 + src/edu/purdue/cs/bloat/tree/IfStmt.java | 140 + src/edu/purdue/cs/bloat/tree/IfZeroStmt.java | 91 + src/edu/purdue/cs/bloat/tree/InitStmt.java | 102 + .../purdue/cs/bloat/tree/InstanceOfExpr.java | 106 + src/edu/purdue/cs/bloat/tree/JsrStmt.java | 95 + src/edu/purdue/cs/bloat/tree/JumpStmt.java | 74 + src/edu/purdue/cs/bloat/tree/LEGatherer.java | 69 + src/edu/purdue/cs/bloat/tree/LabelStmt.java | 98 + src/edu/purdue/cs/bloat/tree/LeafExpr.java | 51 + src/edu/purdue/cs/bloat/tree/LocalExpr.java | 124 + src/edu/purdue/cs/bloat/tree/Makefile | 99 + src/edu/purdue/cs/bloat/tree/MemExpr.java | 51 + src/edu/purdue/cs/bloat/tree/MemRefExpr.java | 56 + src/edu/purdue/cs/bloat/tree/MonitorStmt.java | 91 + src/edu/purdue/cs/bloat/tree/NegExpr.java | 92 + .../purdue/cs/bloat/tree/NewArrayExpr.java | 108 + src/edu/purdue/cs/bloat/tree/NewExpr.java | 90 + .../cs/bloat/tree/NewMultiArrayExpr.java | 123 + src/edu/purdue/cs/bloat/tree/Node.java | 297 ++ .../purdue/cs/bloat/tree/OperandStack.java | 286 ++ .../purdue/cs/bloat/tree/PhiCatchStmt.java | 173 + src/edu/purdue/cs/bloat/tree/PhiJoinStmt.java | 171 + src/edu/purdue/cs/bloat/tree/PhiStmt.java | 83 + .../purdue/cs/bloat/tree/PrintVisitor.java | 727 +++ src/edu/purdue/cs/bloat/tree/RCExpr.java | 71 + .../purdue/cs/bloat/tree/ReplaceVisitor.java | 478 ++ src/edu/purdue/cs/bloat/tree/RetStmt.java | 79 + .../cs/bloat/tree/ReturnAddressExpr.java | 77 + .../purdue/cs/bloat/tree/ReturnExprStmt.java | 76 + src/edu/purdue/cs/bloat/tree/ReturnStmt.java | 65 + src/edu/purdue/cs/bloat/tree/SCStmt.java | 106 + src/edu/purdue/cs/bloat/tree/SRStmt.java | 103 + src/edu/purdue/cs/bloat/tree/ShiftExpr.java | 124 + src/edu/purdue/cs/bloat/tree/StackExpr.java | 84 + .../purdue/cs/bloat/tree/StackManipStmt.java | 156 + .../cs/bloat/tree/StackOptimizationData.java | 354 ++ .../purdue/cs/bloat/tree/StackOptimizer.java | 388 ++ .../purdue/cs/bloat/tree/StaticFieldExpr.java | 92 + src/edu/purdue/cs/bloat/tree/Stmt.java | 56 + src/edu/purdue/cs/bloat/tree/StoreExpr.java | 127 + src/edu/purdue/cs/bloat/tree/SwitchStmt.java | 117 + src/edu/purdue/cs/bloat/tree/Swizzler.java | 142 + src/edu/purdue/cs/bloat/tree/ThrowStmt.java | 76 + src/edu/purdue/cs/bloat/tree/Tree.java | 3257 ++++++++++++++ src/edu/purdue/cs/bloat/tree/TreeVisitor.java | 329 ++ .../purdue/cs/bloat/tree/Type0Visitor.java | 148 + .../purdue/cs/bloat/tree/Type1Visitor.java | 186 + src/edu/purdue/cs/bloat/tree/UCExpr.java | 86 + .../purdue/cs/bloat/tree/UseInformation.java | 73 + src/edu/purdue/cs/bloat/tree/VarExpr.java | 89 + .../purdue/cs/bloat/tree/ZeroCheckExpr.java | 75 + src/edu/purdue/cs/bloat/tree/package.html | 11 + src/edu/purdue/cs/bloat/util/Assert.java | 127 + src/edu/purdue/cs/bloat/util/Graph.java | 976 +++++ src/edu/purdue/cs/bloat/util/GraphNode.java | 108 + .../cs/bloat/util/IdentityComparator.java | 62 + .../cs/bloat/util/ImmutableIterator.java | 66 + src/edu/purdue/cs/bloat/util/Makefile | 31 + .../cs/bloat/util/ResizeableArrayList.java | 82 + src/edu/purdue/cs/bloat/util/UnionFind.java | 197 + src/edu/purdue/cs/bloat/util/package.html | 9 + src/overview.html | 26 + 165 files changed, 46941 insertions(+) create mode 100644 lgpl.license.txt create mode 100644 src/edu/purdue/cs/bloat/Makefile create mode 100644 src/edu/purdue/cs/bloat/cfg/Block.java create mode 100644 src/edu/purdue/cs/bloat/cfg/DominanceFrontier.java create mode 100644 src/edu/purdue/cs/bloat/cfg/DominatorTree.java create mode 100644 src/edu/purdue/cs/bloat/cfg/FlowGraph.java create mode 100644 src/edu/purdue/cs/bloat/cfg/Handler.java create mode 100644 src/edu/purdue/cs/bloat/cfg/Makefile create mode 100644 src/edu/purdue/cs/bloat/cfg/ReplaceTarget.java create mode 100644 src/edu/purdue/cs/bloat/cfg/Subroutine.java create mode 100644 src/edu/purdue/cs/bloat/cfg/VerifyCFG.java create mode 100644 src/edu/purdue/cs/bloat/cfg/package.html create mode 100644 src/edu/purdue/cs/bloat/class.mk create mode 100644 src/edu/purdue/cs/bloat/codegen/CodeGenerator.java create mode 100644 src/edu/purdue/cs/bloat/codegen/Liveness.java create mode 100644 src/edu/purdue/cs/bloat/codegen/Makefile create mode 100644 src/edu/purdue/cs/bloat/codegen/RegisterAllocator.java create mode 100644 src/edu/purdue/cs/bloat/codegen/package.html create mode 100644 src/edu/purdue/cs/bloat/context/BloatContext.java create mode 100644 src/edu/purdue/cs/bloat/context/Makefile create mode 100644 src/edu/purdue/cs/bloat/context/PersistentBloatContext.java create mode 100644 src/edu/purdue/cs/bloat/context/package.html create mode 100644 src/edu/purdue/cs/bloat/diva/InductionVarAnalyzer.java create mode 100644 src/edu/purdue/cs/bloat/diva/Makefile create mode 100644 src/edu/purdue/cs/bloat/diva/package.html create mode 100644 src/edu/purdue/cs/bloat/editor/ClassHierarchy.java create mode 100644 src/edu/purdue/cs/bloat/editor/EditorContext.java create mode 100644 src/edu/purdue/cs/bloat/editor/InstructionAdapter.java create mode 100644 src/edu/purdue/cs/bloat/editor/InstructionVisitor.java create mode 100644 src/edu/purdue/cs/bloat/editor/Makefile create mode 100644 src/edu/purdue/cs/bloat/editor/MemberRef.java create mode 100644 src/edu/purdue/cs/bloat/editor/MethodRef.java create mode 100644 src/edu/purdue/cs/bloat/editor/NameAndType.java create mode 100644 src/edu/purdue/cs/bloat/editor/package.html create mode 100644 src/edu/purdue/cs/bloat/inline/CallGraph.java create mode 100644 src/edu/purdue/cs/bloat/inline/Inline.java create mode 100644 src/edu/purdue/cs/bloat/inline/InlineContext.java create mode 100644 src/edu/purdue/cs/bloat/inline/InlineStats.java create mode 100644 src/edu/purdue/cs/bloat/inline/Makefile create mode 100644 src/edu/purdue/cs/bloat/inline/StackHeightCounter.java create mode 100644 src/edu/purdue/cs/bloat/inline/package.html create mode 100644 src/edu/purdue/cs/bloat/optimize/CodeGen.java create mode 100644 src/edu/purdue/cs/bloat/optimize/ConstantPoolSizePrinter.java create mode 100644 src/edu/purdue/cs/bloat/optimize/Main.java create mode 100644 src/edu/purdue/cs/bloat/optimize/Makefile create mode 100644 src/edu/purdue/cs/bloat/optimize/MethodBloater.java create mode 100644 src/edu/purdue/cs/bloat/optimize/MethodState.java create mode 100644 src/edu/purdue/cs/bloat/optimize/Optimization.java create mode 100644 src/edu/purdue/cs/bloat/optimize/package.html create mode 100644 src/edu/purdue/cs/bloat/reflect/ClassFormatException.java create mode 100644 src/edu/purdue/cs/bloat/reflect/Makefile create mode 100644 src/edu/purdue/cs/bloat/reflect/package.html create mode 100644 src/edu/purdue/cs/bloat/ssa/ComponentVisitor.java create mode 100644 src/edu/purdue/cs/bloat/ssa/Makefile create mode 100644 src/edu/purdue/cs/bloat/ssa/PhiReturnStmt.java create mode 100644 src/edu/purdue/cs/bloat/ssa/SSA.java create mode 100644 src/edu/purdue/cs/bloat/ssa/SSAConstructionInfo.java create mode 100644 src/edu/purdue/cs/bloat/ssa/SSAGraph.java create mode 100644 src/edu/purdue/cs/bloat/ssa/package.html create mode 100644 src/edu/purdue/cs/bloat/tbaa/Makefile create mode 100644 src/edu/purdue/cs/bloat/tbaa/TBAA.java create mode 100644 src/edu/purdue/cs/bloat/tbaa/TypeInference.java create mode 100644 src/edu/purdue/cs/bloat/tbaa/package.html create mode 100644 src/edu/purdue/cs/bloat/tests/BloatTester1.java create mode 100644 src/edu/purdue/cs/bloat/tests/BloatTester2.java create mode 100644 src/edu/purdue/cs/bloat/tests/BloatTester3.java create mode 100644 src/edu/purdue/cs/bloat/tests/Makefile create mode 100644 src/edu/purdue/cs/bloat/trans/DeadCodeElimination.java create mode 100644 src/edu/purdue/cs/bloat/trans/ExprPropagation.java create mode 100644 src/edu/purdue/cs/bloat/trans/Makefile create mode 100644 src/edu/purdue/cs/bloat/trans/NodeComparator.java create mode 100644 src/edu/purdue/cs/bloat/trans/Peephole.java create mode 100644 src/edu/purdue/cs/bloat/trans/SSAPRE.java create mode 100644 src/edu/purdue/cs/bloat/trans/SideEffectChecker.java create mode 100644 src/edu/purdue/cs/bloat/trans/StackOpt.java create mode 100644 src/edu/purdue/cs/bloat/trans/StackOptimization.java create mode 100644 src/edu/purdue/cs/bloat/trans/ValueFolder.java create mode 100644 src/edu/purdue/cs/bloat/trans/ValueFolding.java create mode 100644 src/edu/purdue/cs/bloat/trans/ValueNumbering.java create mode 100644 src/edu/purdue/cs/bloat/trans/package.html create mode 100644 src/edu/purdue/cs/bloat/tree/AddressStoreStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/ArithExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ArrayLengthExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ArrayRefExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/AscendVisitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/Assign.java create mode 100644 src/edu/purdue/cs/bloat/tree/CallExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CallMethodExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CallStaticExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CastExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CatchExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CheckExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/CondExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ConstantExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/DefExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/DefInformation.java create mode 100644 src/edu/purdue/cs/bloat/tree/DescendVisitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/EliminationInformation.java create mode 100644 src/edu/purdue/cs/bloat/tree/Expr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ExprStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/FieldExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/GotoStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/IfCmpStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/IfStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/IfZeroStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/InitStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/InstanceOfExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/JsrStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/JumpStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/LEGatherer.java create mode 100644 src/edu/purdue/cs/bloat/tree/LabelStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/LeafExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/LocalExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/Makefile create mode 100644 src/edu/purdue/cs/bloat/tree/MemExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/MemRefExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/MonitorStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/NegExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/NewArrayExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/NewExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/NewMultiArrayExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/Node.java create mode 100644 src/edu/purdue/cs/bloat/tree/OperandStack.java create mode 100644 src/edu/purdue/cs/bloat/tree/PhiCatchStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/PhiJoinStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/PhiStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/PrintVisitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/RCExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ReplaceVisitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/RetStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/ReturnAddressExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ReturnExprStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/ReturnStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/SCStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/SRStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/ShiftExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/StackExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/StackManipStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/StackOptimizationData.java create mode 100644 src/edu/purdue/cs/bloat/tree/StackOptimizer.java create mode 100644 src/edu/purdue/cs/bloat/tree/StaticFieldExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/Stmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/StoreExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/SwitchStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/Swizzler.java create mode 100644 src/edu/purdue/cs/bloat/tree/ThrowStmt.java create mode 100644 src/edu/purdue/cs/bloat/tree/Tree.java create mode 100644 src/edu/purdue/cs/bloat/tree/TreeVisitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/Type0Visitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/Type1Visitor.java create mode 100644 src/edu/purdue/cs/bloat/tree/UCExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/UseInformation.java create mode 100644 src/edu/purdue/cs/bloat/tree/VarExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/ZeroCheckExpr.java create mode 100644 src/edu/purdue/cs/bloat/tree/package.html create mode 100644 src/edu/purdue/cs/bloat/util/Assert.java create mode 100644 src/edu/purdue/cs/bloat/util/Graph.java create mode 100644 src/edu/purdue/cs/bloat/util/GraphNode.java create mode 100644 src/edu/purdue/cs/bloat/util/IdentityComparator.java create mode 100644 src/edu/purdue/cs/bloat/util/ImmutableIterator.java create mode 100644 src/edu/purdue/cs/bloat/util/Makefile create mode 100644 src/edu/purdue/cs/bloat/util/ResizeableArrayList.java create mode 100644 src/edu/purdue/cs/bloat/util/UnionFind.java create mode 100644 src/edu/purdue/cs/bloat/util/package.html create mode 100644 src/overview.html diff --git a/lgpl.license.txt b/lgpl.license.txt new file mode 100644 index 0000000..5ab7695 --- /dev/null +++ b/lgpl.license.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/edu/purdue/cs/bloat/Makefile b/src/edu/purdue/cs/bloat/Makefile new file mode 100644 index 0000000..350f5b2 --- /dev/null +++ b/src/edu/purdue/cs/bloat/Makefile @@ -0,0 +1,65 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +SUBDIRS = cfg \ + codegen \ + context \ + diva \ + editor \ + inline \ + main \ + optimize \ + reflect \ + ssa \ + tbaa \ + tests \ + trans \ + tree \ + util \ + +all: + @for i in $(SUBDIRS) ""; do \ + if [ "x$$i" != "x" ]; then \ + $(MAKE) -C $$i all; \ + fi; \ + done + +clean: + @for i in $(SUBDIRS) ""; do \ + if [ "x$$i" != "x" ]; then \ + $(MAKE) -C $$i clean; \ + fi; \ + done + +docs: + javadoc -d ../../../../../docs -sourcepath ../../../.. \ + EDU.purdue.cs.bloat.util \ + EDU.purdue.cs.bloat.reflect \ + EDU.purdue.cs.bloat.editor \ + EDU.purdue.cs.bloat.tree \ + EDU.purdue.cs.bloat.cfg \ + EDU.purdue.cs.bloat.ssa \ + EDU.purdue.cs.bloat.tbaa \ + EDU.purdue.cs.bloat.trans \ + EDU.purdue.cs.bloat.diva \ + EDU.purdue.cs.bloat.codegen \ + EDU.purdue.cs.bloat.context \ + EDU.purdue.cs.bloat.optimize \ diff --git a/src/edu/purdue/cs/bloat/cfg/Block.java b/src/edu/purdue/cs/bloat/cfg/Block.java new file mode 100644 index 0000000..653d421 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/Block.java @@ -0,0 +1,442 @@ +/* + * Class: Block + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.tree.StackOptimizationData; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.GraphNode; + +import java.util.*; + +/** + * Block represents a basic block of code used in control flow + * graphs. A basic block is always entered at its beginning and exits at its + * end. That is, its first statement is a label and its last statement is a + * jump. There are no other labels or jumps in between. + *

+ * Each Block knows its parent block and its children in the + * dominator and postdominator trees. It also knows which blocks are in its + * dominance frontier and its postdominance frontier. + * + * @see FlowGraph + * @see DominatorTree + * @see DominanceFrontier + */ +public class Block extends GraphNode { + // There are several "types" of Blocks. A NON_HEADER block is not the + // header of a loop. An IRREDUCIBLE block is one of the headers of an + // irreducible loop. An irriducible loop has more than one entry + // point. They are very rare and are really ugly. The loop + // transformer tries to fix up multiple headers. A REDUCIBLE header is + // a header for a reducible loop. + public static final int NON_HEADER = 0; + + public static final int IRREDUCIBLE = 1; + + public static final int REDUCIBLE = 2; + + FlowGraph graph; // CFG to which this Block belongs + + InstructionHandle label; // This Block's Label + + Tree tree; // Expression tree for this block + + Block domParent; // Block that (immediately) dominates this Block + + Block pdomParent; + + Set domChildren; // Blocks that this Block dominates + + Set pdomChildren; // The postdominator children of this block + + Set domFrontier; // This Block's dominance frontier + + Set pdomFrontier; // This Block's postdominace frontier + + int blockType; // NON_HEADER, IRREDUCIBLE, or REDUCIBLE + + Block header; // The block's loop header + + StackOptimizationData stackOptimizer; // Stack Optimizer + + /** + * Constructor. + * + * @param label + * The block's label. Labels are implemented with BCEL using + * InstructionHandle's attributes this means that a label + * instruction is also the first instruction in the block it + * labels. + * @param graph + * The CFG containing the block. + */ + Block(InstructionHandle label, FlowGraph graph) { + this.label = label; + this.graph = graph; + this.tree = null; + this.header = null; + this.blockType = NON_HEADER; + + FlowGraph.setLabel(label); // Make sure it's set. + // A little unsure here the start of a Block should be the target of a + // BranchInstruction and hasTargeters() should be true. + domParent = null; + pdomParent = null; + + domChildren = new LinkedHashSet(); + pdomChildren = new LinkedHashSet(); + + domFrontier = new LinkedHashSet(); + pdomFrontier = new LinkedHashSet(); + + stackOptimizer = new StackOptimizationData(this); // make + // StackOptimizationData + // object + } + + /** + * Constructor. + * + * I use this method with caution the label should be set as soon as + * possible after constructing the block using setLabel(). + * + * @param graph + * The CFG containing the block. + */ + + Block(FlowGraph graph) { + this.graph = graph; + this.tree = null; + this.header = null; + this.blockType = NON_HEADER; + + // A little unsure here the start of a Block should be the target of a + // BranchInstruction and hasTargeters() should be true, otherwise we may + // be able to set an attribute. + domParent = null; + pdomParent = null; + + domChildren = new LinkedHashSet(); + pdomChildren = new LinkedHashSet(); + + domFrontier = new LinkedHashSet(); + pdomFrontier = new LinkedHashSet(); + + stackOptimizer = new StackOptimizationData(this); // make + // StackOptimizationData + // object + } + + /** + * added so that I can delay adding labels until I have parsed the first + * instruction + * + * @param label + * The InstructionHandle of the first instruction in the block. + */ + public void setLabel(InstructionHandle label) { + FlowGraph.setLabel(label); + this.label = label; + } + + /** + * Returns the stack optimizer for this block. + * + * @return The stack optimizer. + */ + public StackOptimizationData stackOptimizer() { + return stackOptimizer; + } + + /** + * Returns the expression tree for this block. + * + * @return The tree. + */ + public Tree tree() { + return tree; + } + + /** + * Sets the expression tree for this block. + */ + public void setTree(Tree tree) { + this.tree = tree; + } + + /** + * Returns the CFG containing the block. + * + * @return The CFG. + */ + public FlowGraph graph() { + return graph; + } + + /** + * Returns the label associated with this block. + */ + public InstructionHandle label() { + return label; + } + + /** + * Visits the expression tree contained in this block. + */ + public void visitChildren(TreeVisitor visitor) { + if (tree != null) { + tree.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitBlock(this); + } + + /** + * Sets the type of this Block. A Block may have one of three types: + * + *

    + *
  • NON_HEADER: Not the header of any loop + *
  • IRREDUCIBLE: Header of an irreducible loop + *
  • REDUCIBLE: Header of a reducible loop + *
+ * + * A loop is a strongly connected component of a control flow graph. + * A loop's header is the block that dominates all other blocks in + * the loop. A loop is reducible if the only way to enter the loop + * is through the header. + */ + void setBlockType(int blockType) { + this.blockType = blockType; + + if (FlowGraph.DEBUG) { + System.out.println(" Set block type " + this); + } + } + + /** + * Returns the type of this Block. + */ + int blockType() { + return blockType; + } + + public void setHeader(Block header) { + this.header = header; + + if (FlowGraph.DEBUG) { + System.out.println(" Set header " + this); + } + } + + public Block header() { + return header; + } + + /** + * Returns a string representation of this block. + */ + public String toString() { + String s = ""; + } else if (this == graph.init()) { + return s + " init>"; + } else if (this == graph.sink()) { + return s + " sink>"; + } else { + return s + ">"; + } + } + + /** + * Returns the basic blocks that this Block immediately dominates. That is, + * it returns this Block's children in the dominator tree for the CFG. + */ + Collection domChildren() { + return domChildren; + } + + /** + * Returns the immediate dominator of this Block. That is, it returns the + * Block's parent in the dominator tree, its immediate dominator. + */ + Block domParent() { + return domParent; + } + + /** + * Specifies that Block dominates this Block (parent in the dominator tree, + * the immediate dominator). + * + * @param block + * Block that dominates this Block. + */ + void setDomParent(Block block) { + // If this Block already had a dominator specified, remove + // it from its dominator's children. + if (domParent != null) { + domParent.domChildren.remove(this); + } + + domParent = block; + + // Add this Block to its new dominator's children. + if (domParent != null) { + domParent.domChildren.add(this); + } + } + + /** + * Returns whether or this Block dominates another given Block. A node X + * dominates a node Y when every path from the first node in the CFG (Enter) + * to Y must pass through X. + */ + public boolean dominates(Block block) { + Block p = block; + + while (p != null) { + if (p == this) { + return true; + } + p = p.domParent(); + } + + return false; + } + + /** + * Returns the children of this Block in the CFG's postdominator tree. + */ + Collection pdomChildren() { + return pdomChildren; + } + + /** + * Returns the parent of this Block in the CFG's postdominator tree. + */ + Block pdomParent() { + return pdomParent; + } + + /** + * Sets this Block's parent in the postdominator tree. + */ + void setPdomParent(Block block) { + if (pdomParent != null) { + pdomParent.pdomChildren.remove(this); + } + + pdomParent = block; + + if (pdomParent != null) { + pdomParent.pdomChildren.add(this); + } + } + + /** + * Determines whether or not this block postdominates a given block. A block + * X is said to postdominate a block Y when every path from Y to the last + * node in the CFG (Exit) passes through X. This relationship can be thought + * of as the reverse of dominance. That is, X dominates Y in the reverse + * CFG. + * + * @see DominatorTree + */ + public boolean postdominates(Block block) { + Block p = block; + + while (p != null) { + if (p == this) { + return true; + } + p = p.pdomParent(); + } + + return false; + } + + /** + * Returns the blocks that are in this block's dominance frontier. The + * dominance frontier of a node X in a CFG is the set of all nodes Y such + * that X dominates a predacessor of Y, but does not strictly dominate Y. + * Nodes in the dominance frontier always have more than one parent (a + * join). + * + * @see DominanceFrontier + */ + Collection domFrontier() { + Assert.isTrue(domFrontier != null); + return domFrontier; + } + + /** + * Returns the postdominance frontier for this node. A postdominace frontier + * is essentially the same as a dominace frontier, but the postdominance + * relationship is used instead of the dominance relationship. + */ + Collection pdomFrontier() { + Assert.isTrue(pdomFrontier != null); + return pdomFrontier; + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/DominanceFrontier.java b/src/edu/purdue/cs/bloat/cfg/DominanceFrontier.java new file mode 100644 index 0000000..b9f006e --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/DominanceFrontier.java @@ -0,0 +1,172 @@ +/* + * Class: DominanceFrontier + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import java.util.*; + +/** + * DominanceFrontier is used to calculate the dominance frontier + * of each node in a control flow graph. + *

+ * The dominance frontier of a node x is the set of all nodes w such + * that x dominates a predacessor of w, but does not strictly dominate w. + * Basically, nodes in the dominance frontier have one parent that is + * dominated by x and at least one parent that is not dominated by x. + *

+ * DominanceFrontier can be used to calculate both the dominance + * (forward) and the postdominance (reverse) frontiers for a control flow graph. + * + * @see FlowGraph + */ + +public class DominanceFrontier { + /** + * Calculates the dominance frontier for a cfg and notifies the blocks in it + * appropriately. + * + * @param graph + * The cfg to operate on + * @param reverse + * Do we calculate the postdominance frontier? + */ + public static void buildFrontier(FlowGraph graph, boolean reverse) { + if (!reverse) { + calcFrontier(graph.source(), graph, reverse); + } else { + calcFrontier(graph.sink(), graph, reverse); + } + } + + /** + * Recursively traverses the cfg and builds up the dominance frontier. + *

+ * A block n's dominance frontier is the union of two sets of nodes. The + * first set is the nodes in the dominance frontier of the nodes that n + * dominates that are not dominated by n's immediate dominator. The second + * set consists of the successors of n that are not strictly dominated by n. + * + * @param block + * The block to start from (either source or sink) + * @param graph + * The cfg from which to get blocks + * @param reverse + * Do we calculate the dominance or postdominance frontier? + * + * @return The blocks in the (post)dominance frontier of block + */ + private static LinkedList calcFrontier(Block block, FlowGraph graph, + boolean reverse) { + // local is an array of Blocks that are in block's dominance + // frontier. It is indexed by the block's pre-order index. I + // suppose an array is used so that no block is added to the + // dominance frontier twice. + Block[] local = new Block[graph.size()]; + + Iterator children; // The blocks that are dominated by block + + if (!reverse) { + children = block.domChildren().iterator(); + } else { + children = block.pdomChildren().iterator(); + } + + // Recursively calculate the nodes in the dominance frontier of + // block that are not dominated by block's immediate dominator + while (children.hasNext()) { + Block child = (Block) children.next(); + + LinkedList df = calcFrontier(child, graph, reverse); + + Iterator e = df.iterator(); + + while (e.hasNext()) { + Block dfChild = (Block) e.next(); + + if (!reverse) { + if (block != dfChild.domParent()) + local[graph.preOrderIndex(dfChild)] = dfChild; + + } else { + if (block != dfChild.pdomParent()) + local[graph.preOrderIndex(dfChild)] = dfChild; + } + } + } + + Iterator succs = reverse ? graph.preds(block).iterator() : graph.succs( + block).iterator(); + + // Caculate the successors of block that are not strictly + // dominated by block. + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + // If block is not the immediate (post)dominator of its + // successor, add it to block's dominance frontier. + if (!reverse) { + if (block != succ.domParent()) + local[graph.preOrderIndex(succ)] = succ; + + } else { + if (block != succ.pdomParent()) + local[graph.preOrderIndex(succ)] = succ; + } + } + + LinkedList v = new LinkedList(); // The dominance frontier + + for (int i = 0; i < local.length; i++) { + if (local[i] != null) { + v.add(local[i]); + } + } + + // Set block's (post)dominance frontier + if (!reverse) { + block.domFrontier().clear(); + block.domFrontier().addAll(v); + } else { + block.pdomFrontier().clear(); + block.pdomFrontier().addAll(v); + } + + return v; + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/DominatorTree.java b/src/edu/purdue/cs/bloat/cfg/DominatorTree.java new file mode 100644 index 0000000..bdef062 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/DominatorTree.java @@ -0,0 +1,399 @@ +/* + * Class: DominatorTree + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import java.util.*; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * DominatorTree finds the dominator tree of a FlowGraph. + *

+ * The algorithm used is Purdum-Moore. It isn't as fast as Lengauer-Tarjan, but + * it's a lot simpler. + * + * @see FlowGraph + * @see Block + */ +public class DominatorTree { + public static boolean DEBUG = false; + + /** + * Calculates what vertices dominate other verices and notify the basic + * Blocks as to who their dominator is. + * + * @param graph + * The cfg that is used to find the dominator tree. + * @param reverse + * Do we go in revsers? That is, are we computing the dominatance + * (false) or postdominance (true) tree. + * @see Block + */ + public static void buildTree(FlowGraph graph, boolean reverse) { + int size = graph.size(); // The number of vertices in the cfg + + Map snkPreds = new LinkedHashMap(); // The predacessor vertices from the + // sink + + // Determine the predacessors of the cfg's sink node + insertEdgesToSink(graph, snkPreds, reverse); + + // Get the index of the root + int root = reverse ? graph.preOrderIndex(graph.sink()) : graph + .preOrderIndex(graph.source()); + + Assert.isTrue(0 <= root && root < size); + + // Bit matrix indicating the dominators of each vertex. + // If bit j of dom[i] is set, then node j dominates node i. + BitSet[] dom = new BitSet[size]; + + // A bit vector of all 1's + BitSet ALL = new BitSet(size); + + for (int i = 0; i < size; i++) { + ALL.set(i); + } + + // Initially, all the bits in the dominance matrix are set, except + // for the root node. The root node is initialized to have itself + // as an immediate dominator. + // + for (int i = 0; i < size; i++) { + BitSet blockDoms = new BitSet(size); + dom[i] = blockDoms; + + if (i != root) { + blockDoms.or(ALL); + } else { + blockDoms.set(root); + } + } + + // Did the dominator bit vector array change? + boolean changed = true; + + while (changed) { + changed = false; + + // Get the basic blocks contained in the cfg + Iterator blocks = reverse ? graph.postOrder().iterator() : graph + .preOrder().iterator(); + + // Compute the dominators of each node in the cfg. We iterate + // over every node in the cfg. The dominators of a node, x, are + // found by taking the intersection of the dominator bit vectors + // of each predacessor of x and unioning that with x. This + // process is repeated until no changes are made to any + // dominator + // bit vector. + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + int i = graph.preOrderIndex(block); + + Assert.isTrue(0 <= i && i < size, "Unreachable block " + block); + + // We already know the dominators of the root, keep looking + if (i == root) { + continue; + } + + BitSet oldSet = dom[i]; + BitSet blockDoms = new BitSet(size); + blockDoms.or(oldSet); + + // blockDoms := intersection of dom(pred) for all pred(block). + Collection preds = reverse ? graph.succs(block) : graph + .preds(block); + + Iterator e = preds.iterator(); + + // Find the intersection of the dominators of block's + // predacessors. + while (e.hasNext()) { + Block pred = (Block) e.next(); + + int j = graph.preOrderIndex(pred); + Assert.isTrue(j >= 0, "Unreachable block " + pred); + + blockDoms.and(dom[j]); + } + + // Don't forget to account for the sink node if block is a + // leaf node. Appearantly, there are not edges between + // leaf nodes and the sink node! + preds = (Collection) snkPreds.get(block); + + if (preds != null) { + e = preds.iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + + int j = graph.preOrderIndex(pred); + Assert.isTrue(j >= 0, "Unreachable block " + pred); + + blockDoms.and(dom[j]); + } + } + + // Include yourself in your dominators?! + blockDoms.set(i); + + // If the set changed, set the changed bit. + if (!blockDoms.equals(oldSet)) { + changed = true; + dom[i] = blockDoms; + } + } + } + + // Once we have the predacessor bit vectors all squared away, we can + // determine which vertices dominate which vertices. + + Iterator blocks = graph.nodes().iterator(); + + // Initialize each block's (post)dominator parent and children + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + if (!reverse) { + block.setDomParent(null); + block.domChildren().clear(); + } else { + block.setPdomParent(null); + block.pdomChildren().clear(); + } + } + + blocks = graph.nodes().iterator(); + + // A block's immediate dominator is its closest dominator. So, we + // start with the dominators, dom(b), of a block, b. To find the + // imediate dominator of b, we remove all blocks from dom(b) that + // dominate any block in dom(b). + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + int i = graph.preOrderIndex(block); + + Assert.isTrue(0 <= i && i < size, "Unreachable block " + block); + + if (i == root) { + if (!reverse) { + block.setDomParent(null); + } else { + block.setPdomParent(null); + } + + } else { + // Find the immediate dominator + // idom := dom(block) - dom(dom(block)) - block + BitSet blockDoms = dom[i]; + + // print(graph, reverse, "dom set", i, blockDoms); + + BitSet idom = new BitSet(size); + idom.or(blockDoms); + idom.clear(i); + + for (int j = 0; j < size; j++) { + if (i != j && blockDoms.get(j)) { + BitSet domDomBlocks = dom[j]; + + // idom = idom - (domDomBlocks - {j}) + BitSet b = new BitSet(size); + b.or(domDomBlocks); + b.xor(ALL); + b.set(j); + idom.and(b); + } + } + + Block parent = null; + + // A block should only have one immediate dominator. + for (int j = 0; j < size; j++) { + if (idom.get(j)) { + Block p = (Block) graph.preOrder().get(j); + + Assert.isTrue(parent == null, block + + " has more than one immediate dominator: " + + parent + " and " + p); + + parent = p; + } + } + + Assert.isTrue(parent != null, block + " has 0 immediate " + + (reverse ? "postdominators" : "dominators")); + + if (!reverse) { + if (DEBUG) { + System.out.println(parent + " dominates " + block); + } + + block.setDomParent(parent); + + } else { + if (DEBUG) { + System.out.println(parent + " postdominates " + block); + } + + block.setPdomParent(parent); + } + } + } + } + + /** + * Determines which nodes are predacessors of a cfg's sink node. Creates a + * Map that maps the sink node to its predacessors (or the leaf nodes to the + * sink node, their predacessor, if we're going backwards). + * + * @param graph + * The cfg to operate on. + * @param preds + * A mapping from leaf nodes to their predacessors. The exact + * semantics depend on whether or not we are going forwards. + * @param reverse + * Are we computing the dominators or postdominators? + */ + private static void insertEdgesToSink(FlowGraph graph, Map preds, + boolean reverse) { + BitSet visited = new BitSet(); // see insertEdgesToSinkDFS + BitSet returned = new BitSet(); + + visited.set(graph.preOrderIndex(graph.source())); + + insertEdgesToSinkDFS(graph, graph.source(), visited, returned, preds, + reverse); + } + + /** + * This method determines which nodes are the predacessor of the sink node + * of a cfg. A depth-first traversal of the cfg is performed. When a leaf + * node (that is not the sink node) is encountered, add an entry to the + * preds Map. + * + * @param graph + * The cfg being operated on. + * @param block + * The basic Block to start at. + * @param visited + * Vertices that were visited + * @param returned + * Vertices that returned + * @param preds + * Maps a node to a HashSet representing its predacessors. In the + * case that we're determining the dominace tree, preds maps the + * sink node to its predacessors. In the case that we're + * determining the postdominance tree, preds maps the sink node's + * predacessors to the sink node. + * @param reverse + * Do we go in reverse? + */ + private static void insertEdgesToSinkDFS(FlowGraph graph, Block block, + BitSet visited, BitSet returned, Map preds, boolean reverse) { + boolean leaf = true; // Is a vertex a leaf node? + + // Get the successors of block + Iterator e = graph.succs(block).iterator(); + + while (e.hasNext()) { + Block succ = (Block) e.next(); + + // Determine index of succ vertex in a pre-order traversal + int index = graph.preOrderIndex(succ); + Assert.isTrue(index >= 0, "Unreachable block " + succ); + + if (!visited.get(index)) { + // If the successor block hasn't been visited, visit it + visited.set(index); + insertEdgesToSinkDFS(graph, succ, visited, returned, preds, + reverse); + returned.set(index); + leaf = false; + + } else if (returned.get(index)) { + // Already visited and returned, so a descendent of succ + // has an edge to the sink. + leaf = false; + } + } + + if (leaf && block != graph.sink()) { + // If we're dealing with a leaf node that is not the sink, set + // up its predacessor set. + + if (!reverse) { + // If we're going forwards (computing dominators), get the + // predacessor vertices from the sink + Set p = (Set) preds.get(graph.sink()); + + // If there are no (known) predacessors, make a new + // LinkedHashSet to + // store them and register it in the pred Map. + if (p == null) { + p = new LinkedHashSet(); + preds.put(graph.sink(), p); + } + + // The block is in the predacessors of the sink + p.add(block); + + } else { + // If we're going backwards, get the block's predacessors + Set p = (Set) preds.get(block); + + if (p == null) { + p = new LinkedHashSet(); + preds.put(block, p); + } + + // Add the sink vertex to the predacessors of the block + p.add(graph.sink()); + } + } + } + +} diff --git a/src/edu/purdue/cs/bloat/cfg/FlowGraph.java b/src/edu/purdue/cs/bloat/cfg/FlowGraph.java new file mode 100644 index 0000000..95d1c79 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/FlowGraph.java @@ -0,0 +1,3688 @@ +/* + * Class: FlowGraph + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.codegen.CodeGenerator; +import edu.purdue.cs.bloat.trans.SSAPRE; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfCmpStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.IfZeroStmt; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.JumpStmt; +import edu.purdue.cs.bloat.tree.LabelStmt; +import edu.purdue.cs.bloat.tree.LeafExpr; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.OperandStack; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PrintVisitor; +import edu.purdue.cs.bloat.tree.ReplaceVisitor; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.Graph; +import edu.purdue.cs.bloat.util.GraphNode; +import edu.purdue.cs.bloat.util.ImmutableIterator; +import edu.purdue.cs.bloat.util.ResizeableArrayList; +import edu.purdue.cs.bloat.util.UnionFind; + +import java.io.*; +import java.util.*; + +/** + * FlowGraph constructs and represents a Control Flow Graph (CFG) used for + * analyzing a method. It consists of the basic blocks of a method. + *

+ * + * + * @see MethodEditor + * @see Block + */ +public class FlowGraph extends Graph { + public static final int PEEL_NO_LOOPS = 0; + + public static final int PEEL_ALL_LOOPS = -1; + + public static int PEEL_LOOPS_LEVEL = 1; + + public static boolean DEBUG = false; + + public static boolean DB_GRAPHS = false; + + public static boolean PRINT_GRAPH = false; + + MethodGen method; // The method that we create a CFG for. + + public InstructionList instructions; + + // Has to be public as Peephole and StackOpt need to modify the + // InstructionList --Arrin + + int labelIndex = 0; + + int maxLocals = 0; + + Map subroutines; // Mapping between a Subroutine and its + + // entry Block + List catchBlocks; // The Blocks that begin exception handlers + + Map handlers; // Maps first block of exception handler to + + // its Handler object. + + Block srcBlock; // The first (source) basic Block in this method + + Block snkBlock; // The "last" Block (where throw and return go) + + Block iniBlock; // Block that handles initialization of parameters + + // A trace is a series of basic blocks that have the following + // properties: + // 1) Blocks that end with a conditional jump are followed by the block + // that is executed when the conditon is false. + // 2) Where possible, blocks ending with a unconditional jump are + // followed by the block that is the target of that unconditional jump. + // Property 1) allows conditionals that resolve to false to "fall + // through" + // and property 2) allows for the removal of labels. Typically, + // bytecode will already be in trace form. + + List trace; // All of the basic Blocks except source and sink + + Graph loopTree; // A graph representing the loop nesting in + + // the method. + + // Modification counts for the dominator tree and the loop tree. + // Recall that superclass Graph maintains the modifications counts + // on nodes and edges. + int domEdgeModCount; + + int loopEdgeModCount; + + // The maximum (greatest) loop depth/level + int maxLoopDepth = 0; + + // BLOAT has Label instructions to denote the start of a basic block to + // provide + // this in BCEL we make use of BCEL's attributes in InstructionHandle + // with the key "Label". + // This method sets an instruction as a Label, the original instruction + // still exists and + // is pointed to by the same InstructionHandle which makes + // the porting of StackOpt and Peephole tricky --Arrin + + public static void setLabel(InstructionHandle ih) { + ih.addAttribute("Label", new Boolean(true)); + } + + // This method unsets the "Label" attribute --Arrin + public static void unsetLabel(InstructionHandle ih) { + ih.addAttribute("Label", new Boolean(false)); + } + + // A method to test if this instruction is a Label. --Arrin + public static boolean label(InstructionHandle ih) { + Object label = null; + if (ih != null && (label = ih.getAttribute("Label")) != null) + return ((Boolean) label).booleanValue(); + else + return false; + } + + /** + * Method added to convert a BCEL InstructionList to a BLOAT one This method + * inserts Labels in the appropriate places it does this by identifying + * instructions which cause jumps and adding a label at the target of those + * jumps. -- Arrin + * + * @param method + * The method for which we want insert labels. + */ + public static void buildBLOATInstructionList(MethodGen method) { + + InstructionHandle ih = method.getInstructionList().getStart(); + setLabel(ih); // first instruction starts a basic block + setLabel(method.getInstructionList().append(new NOP())); // after the + // last + // instruction + // is a + // label + while (ih != method.getInstructionList().getEnd() && ih != null) { + Instruction instruction = ih.getInstruction(); + if (instruction instanceof BranchInstruction// ){ + || instruction instanceof UnconditionalBranch) { + if (instruction instanceof BranchInstruction) { + BranchInstruction binst = (BranchInstruction) instruction; + if (binst instanceof GotoInstruction) { + setLabel(binst.getTarget()); + } else if (binst instanceof IfInstruction) { + setLabel(binst.getTarget()); + } else if (binst instanceof JsrInstruction) { + JsrInstruction jSR = (JsrInstruction) binst; + setLabel(jSR.getTarget()); + } else if (binst instanceof Select) { + Select select = (Select) binst; + InstructionHandle[] targets = select.getTargets(); + for (int j = 0; j < targets.length; j++) { + setLabel(targets[j]); + } + setLabel(select.getTarget()); + } + ih = ih.getNext(); + setLabel(ih); + } else { + ih = ih.getNext(); + setLabel(ih); + } + } else { + ih = ih.getNext(); + } + } + CodeExceptionGen[] exptns = method.getExceptionHandlers(); + for (int i = 0; i < exptns.length; i++) { + // setLabel(exptns[i].getEndPC()); + setLabel(exptns[i].getStartPC()); + setLabel(exptns[i].getHandlerPC()); + } + } + + public int addLocal(Type type) { + int index = maxLocals; + + maxLocals += type.getSize(); + return (index); + } + + /** + * The removeNOPs in InstructionList will not maintain the attributes set in + * the InstructionHandles of the deleted NOP instructions. This a method + * that will remove NOPs but maintain the attribute "Label". NOPs are + * inserted during codegeneration to denote the targets of + * BranchInsructions. Removing NOPs might speed up Peephole and StackOpt + * marginally. + * + * @param method + * The method to remove NOPs from + */ + public static void removeNOPs(MethodGen method) { + InstructionList instructions = method.getInstructionList(); + if (instructions != null) { + InstructionHandle next; + /* + * Check branch instructions. + */ + for (InstructionHandle ih = instructions.getStart(); ih != null; ih = next) { + next = ih.getNext(); + + if ((next != null) && (ih.getInstruction() instanceof NOP)) { + try { + boolean label = FlowGraph.label(ih); + next.addAttribute("Label", new Boolean(label + || FlowGraph.label(next))); + // The next instruction could be a label even is this + // one isn't so we + // don't want to over write that value + instructions.delete(ih); + } catch (TargetLostException e) { + InstructionHandle[] targets = e.getTargets(); + + for (int i = 0; i < targets.length; i++) { + InstructionTargeter[] targeters = targets[i] + .getTargeters(); + + for (int j = 0; j < targeters.length; j++) + targeters[j].updateTarget(targets[i], next); + } + } + } + } + } + } + + private void db(String s) { + if (DEBUG || DB_GRAPHS) + System.out.println(s); + } + + /** + * Constructor. + * + * @param method + * The method to create the CFG for. + */ + public FlowGraph(MethodGen method) { + this.method = method; + this.instructions = method.getInstructionList(); + buildBLOATInstructionList(method); + // this puts Label information in InstructionHandle's attributes to + // allow us to tell which instructions begin basicBlocks + + subroutines = new LinkedHashMap(); + catchBlocks = new ArrayList(method.getExceptionHandlers().length); + handlers = new LinkedHashMap( + method.getExceptionHandlers().length * 2 + 1); + trace = new LinkedList(); + maxLocals = method.getMaxLocals(); + + srcBlock = newBlock(instructions.append(new NOP())); + iniBlock = newBlock(instructions.append(new NOP())); + snkBlock = newBlock(instructions.append(new NOP())); + + trace.add(iniBlock); + + // If this method is empty(!) just make some default cfg edges + if (method.getInstructionList().getLength() == 0) { + addEdge(srcBlock, iniBlock); + addEdge(iniBlock, snkBlock); + addEdge(srcBlock, snkBlock); + + buildSpecialTrees(null); + + return; + } + instructions.setPositions(true); + buildBlocks(); + removeUnreachable(); + + // Make sure any labels in the removed blocks are saved. + saveLabels(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + System.out.println("---------- After building tree:"); + print(System.out); + System.out.println("---------- end print after building tree"); + } + } + + /** + * Returns the maximum loop depth (also the maximum loop height) in the + * control flow graph. + */ + public int maxLoopDepth() { + return (maxLoopDepth); + } + + /** + * Sets up the control flow graph. Computes the dominators and the dominance + * frontier, cleans up the tree, works with the loops, inserts stores to aid + * copy and constant propagation as well as code generation. + */ + public void initialize() { + if (method.getInstructionList().getLength() == 0) { + computeDominators(); + buildLoopTree(); + return; + } + + // Determine which vertices dominate which vertices, update the blocks + // in the cfg appropriately + computeDominators(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After computing dominators (Begin)"); + this.print(System.out); + db("------ After computing dominators (End)"); + } + + // Make sure that no block is both an entry block and a return target. + splitPhiBlocks(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting phi blocks (Begin)"); + this.print(System.out); + db("------ After splitting phi blocks (End)"); + } + + removeUnreachable(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 1 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 1 (End)"); + } + + splitIrreducibleLoops(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting irreduciable loops (Begin)"); + this.print(System.out); + db("------ After splitting irreducible loops (End)"); + } + + removeUnreachable(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 2 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 2 (End)"); + } + + splitReducibleLoops(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting reducible loops (Begin)"); + this.print(System.out); + db("------ After splitting reducible loops (End)"); + } + + removeUnreachable(); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 3 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 3 (End)"); + } + + buildLoopTree(); + + peelLoops(PEEL_LOOPS_LEVEL); + + if (DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After peeling loops (Begin)"); + this.print(System.out); + db("------ After peeling loops (End)"); + } + + removeCriticalEdges(); + removeUnreachable(); + + // Insert stores after conditional branches to aid copy and constant + // propagation. + insertConditionalStores(); + + // Insert stores at the beginnings of protected regions to aid + // code generation for PhiCatchStmts. + insertProtectedRegionStores(); + + if (FlowGraph.DEBUG) { + System.out.println("---------- After splitting loops:"); + print(System.out); + System.out.println("---------- end print after splitting loops"); + } + } + + /** + * Returns the loop tree for the method modeled by this flow graph. The loop + * tree represents the nesting of the loops in a method. The procedure is at + * the root of the loop tree. Nested loops are represented by a parent and + * child relationship. + */ + public Graph loopTree() { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + return loopTree; + } + + /** + * Builds the loop tree. + * + * @see #loopTree + * @see LoopNode + */ + public void buildLoopTree() { + db(" Building loop tree"); + + loopEdgeModCount = edgeModCount; + + removeUnreachable(); + + setBlockTypes(); + + final LoopNode root = new LoopNode(srcBlock); + + // loopTree has one root, the node containing the srcBlock. + loopTree = new Graph() { + public Collection roots() { + ArrayList r = new ArrayList(1); + r.add(root); + return r; + } + }; + + loopTree.addNode(srcBlock, root); + + Iterator iter = nodes().iterator(); + + // Iterate over the blocks in the control flow graph. Add blocks + // to the loop tree node corresponding to their loop header. If + // the block itself is a header block, make a new loop tree node + // for it. An edge in the loop tree is added from the outer loop + // tree node to the inner loop tree node. + while (iter.hasNext()) { + Block block = (Block) iter.next(); + Block header = block.header(); + + if (header != null) { + LoopNode headerLoop = (LoopNode) loopTree.getNode(header); + + if (headerLoop == null) { + headerLoop = new LoopNode(header); + loopTree.addNode(header, headerLoop); + } + + headerLoop.elements.add(block); + + if (block.blockType() != Block.NON_HEADER) { + LoopNode loop = (LoopNode) loopTree.getNode(block); + + if (loop == null) { + loop = new LoopNode(block); + loopTree.addNode(block, loop); + } + + // Edges go from outer loops in. + loopTree.addEdge(headerLoop, loop); + } + } + } + + // Iterate over the loop tree from the bottom up and determine + // each node's level. Level 0 occurs at the leaf nodes. + iter = loopTree.postOrder().iterator(); + + while (iter.hasNext()) { + LoopNode loop = (LoopNode) iter.next(); + + // The level of the node is max(level(succs)) + 1. + int level = 0; + + Iterator succs = loopTree.succs(loop).iterator(); + + while (succs.hasNext()) { + LoopNode inner = (LoopNode) succs.next(); + + if (level < inner.level) { + level = inner.level; + } + } + + loop.level = level + 1; + } + + // Iterate over the loop tree from the top down and determine each + // node's depth. Depth 0 occurs at the root node. + iter = loopTree.preOrder().iterator(); + + while (iter.hasNext()) { + LoopNode loop = (LoopNode) iter.next(); + + // The depth of the node is depth(pred) + 1. + Iterator preds = loopTree.preds(loop).iterator(); + + if (preds.hasNext()) { + LoopNode outer = (LoopNode) preds.next(); + loop.depth = outer.depth + 1; + + } else { + loop.depth = 0; + } + } + } + + /** + * Creates the basic blocks for the method that is the cfg. + * + */ + private void buildBlocks() { + db(" Building blocks"); + + // Go through the code, find each Label that starts a block, create + // a Block for that Label, and add it to the trace. + Iterator iter = instructions.iterator(); + // while((InstructionHandle)iter.next() != snkBlock.label(); + + while (iter.hasNext()) { + InstructionHandle inst = (InstructionHandle) iter.next(); + if (inst != srcBlock.label() && inst != iniBlock.label() + && inst != snkBlock.label()) + if (label(inst)) { + trace.add(newBlock(inst)); + } + } + + InstructionHandle last = null; + Block currBlock = iniBlock; + Block firstBlock = null; + + int i = 0; + iter = instructions.iterator(); + + while (iter.hasNext()) { + InstructionHandle curr = (InstructionHandle) iter.next(); + + if (label(curr)) { + + Block nextBlock = (Block) getNode(curr); + + // If the last instruction we saw was a jsr, establish a path + // between the current block and the block that contains the + // subroutine (operand to the jsr). + + if (last != null + && (last.getInstruction() instanceof JsrInstruction)) { + JsrInstruction lastJSR = (JsrInstruction) last + .getInstruction(); + Block target = (Block) getNode(lastJSR.getTarget()); + Subroutine sub = (Subroutine) subroutines.get(target); + sub.addPath(currBlock, nextBlock); + } + + currBlock = nextBlock; // Go on to next block + + if (firstBlock == null) { + firstBlock = currBlock; + } + } + Instruction currInst = curr.getInstruction(); + + last = curr; + + // Call setsubEntry to maintain a mapping between the entry + // block of a Subroutine and the Subroutine itself + if (currInst instanceof JsrInstruction) { + InstructionHandle label = ((JsrInstruction) currInst) + .getTarget(); + Block target = (Block) getNode(label); + + if (!subroutines.containsKey(target)) { + Subroutine sub = new Subroutine(this); + setSubEntry(sub, target); + } + } + + i++; // Go to next instruction + } + + // Start the tedious process of building the expression trees for + // the basic blocks + buildTrees(firstBlock); + } + + /** + * Build the trees for the blocks, construct subroutines and add edges in + * the flow graph. + * + * There is a edge from the source block to the init block, to the entry of + * each subroutine and to the catch block of each exception handler. + * + * After building trees for all nodes reachable from the source, blocks with + * null trees are removed since they are unreachable. + * + * Edges are added to the sink block from each node ending in a return, a + * throw, or a ret. + * + * In addition, an edge is added to the sink block from each node ending in + * unconditional branch to an ancestor. These edges are used to allow the + * post dominator tree to be contructed in the presence of loops. + * + * @param firstBlock + * The first block of code in this method. + */ + private void buildTrees(Block firstBlock) { + db(" Building trees for " + firstBlock); + + // Maps a "catch block" (beginning of exception handler that + // stores the exception) to a "catch body" (the code immediately + // follows the "catch block" -- the rest of the handler). + LinkedHashMap catchBodies = new LinkedHashMap(method + .getExceptionHandlers().length * 2 + 1); + List list = Arrays.asList(method.getExceptionHandlers()); + Iterator tryCatches = list.iterator(); + + while (tryCatches.hasNext()) { + CodeExceptionGen excpt = (CodeExceptionGen) tryCatches.next(); + + // We create two blocks for each handler. + // catchBlock is the handler target. It contains the code + // which saves the exception on the operand stack. + // catchBody is the block following the handler target. + // It contains the code for the exception handler body. + // We need to split these two blocks so that the handler target + // cannot possibly be a loop header. + + // This block will be the target of the exception body. + Block catchBlock = newBlock(instructions.insert(excpt + .getHandlerPC(), new NOP())); + + // This block will be the target of the exception handler. + // Additions to find the info in BCEL's structures + Block catchBody = (Block) getNode(excpt.getHandlerPC()); + // Block catchBlock = (Block) getNode(excpt.getHandlerPC()); + + catchBodies.put(catchBlock, catchBody); + + // Make sure we include the new block in any protected area + // containing the catch body. + // InstructionHandle pos = excpt.getHandlerPC(); + + addEdge(catchBlock, catchBody); + trace.add(trace.indexOf(catchBody), catchBlock); + + Type type = excpt.getCatchType(); + + if (type == null) { + type = Type.NULL; + } + + catchBlocks.add(catchBlock); + + // Save the exception to the stack. + StackExpr lhs = new StackExpr(0, Type.THROWABLE); + CatchExpr rhs = new CatchExpr(type, Type.THROWABLE); + StoreExpr store = new StoreExpr(lhs, rhs, Type.THROWABLE); + + // Build the tree for the exception handler target block. + Tree tree = new Tree(catchBlock, new OperandStack()); + catchBlock.setTree(tree); + tree.addStmt(new ExprStmt(store)); + tree.addStmt(new GotoStmt(catchBody)); + + // Create the Handler. + InstructionHandle start = excpt.getStartPC(); + Handler handler = new Handler(catchBlock, type); + handlers.put(catchBlock, handler); + + // Examine all of the basic blocks in this CFG. If the block's + // offset into the code is between the start and end points of + // the TryCatch, then it is a protected block. So, the block + // should be added to the Handler's list of protected blocks. + + InstructionHandle index = start; + while (index != null && index != catchBlock.label()) { + if (label(index)) { + Block block = (Block) getNode(index); + if (block != null && block != catchBlock + && block != catchBody) { + handler.protectedBlocks().add(block); + } + } + index = index.getNext(); + } + } + + addEdge(srcBlock, iniBlock); + addEdge(srcBlock, snkBlock); + addEdge(iniBlock, firstBlock); + + buildSpecialTrees(catchBodies); + + // Build the trees for the blocks reachable from the firstBlock. + buildTreeForBlock(firstBlock, iniBlock.tree().stack(), null, + catchBodies); + } + + /** + * Insert stores after conditional branches to aid copy and constant + * propagation. + * + * If a+b and c+d are non-leaf expressions, we convert: + * + * if (a+b == c+d) X else Y + * + * to: + * + * if ((e = a+b) == (f = c+d)) e = f X else Y + * + * We can't do this for reference types since we may loose type information. + * Consider: + * + * class A {} class B extends A { void foo(); } + * + * L := someA(); M := someB(); + * + * if (L == M) { M.foo(); } + * + * --> + * + * if (L == M) { M = L; // M now has type A, not B M.foo(); } + * + */ + public void insertConditionalStores() { + db(" Inserting conditional stores"); + + Iterator blocks = new ImmutableIterator(nodes()); + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + Stmt last = block.tree().lastStmt(); + if (last == null) { + continue; + } + if (last instanceof IfCmpStmt) { + IfCmpStmt stmt = (IfCmpStmt) last; + + // Where do we insert the conditional? (The target of the true + // or false branch.) + Block target = null; + + // Exclude targets which are mentioned more than once. + // This prevents: + // + // if (i == j) goto L + // else goto L + // L: + // i = j; + // + // Note: this shouldn't happen with IfStmts after critical. + // edges are removed. + // + if (stmt.trueTarget() == stmt.falseTarget()) { + continue; + } + + // Ignore all comparison operations except EQ and NE + if (stmt.comparison() == IfStmt.EQ) { + target = stmt.trueTarget(); + + } else if (stmt.comparison() == IfStmt.NE) { + target = stmt.falseTarget(); + } + + if (target != null) { + Expr left = stmt.left(); + Expr right = stmt.right(); + + // If either the left expression or the right expresion + // is a + // reference, then we can't do anything. See above. + if (!(left.type() instanceof ReferenceType) + && !(right.type() instanceof ReferenceType)) { + + // If either of the expression is a leaf expression + // (meaning that it has no children expressions), make a + // new local variable to store the result of the + // expression. Replace the expression with a StoreExpr + // that stores the result of the expression into the + // local + // variable. + + if (!(left instanceof LeafExpr)) { + int index = addLocal(left.type()); + LocalExpr tmp = new LocalExpr(index, left.type()); + Expr copy = (Expr) left.clone(); + copy.setDef(null); + left.replaceWith(new StoreExpr(tmp, copy, left + .type())); + left = tmp; + } + + if (!(right instanceof LeafExpr)) { + int index = addLocal(right.type()); + LocalExpr tmp = new LocalExpr(index, right.type()); + Expr copy = (Expr) right.clone(); + copy.setDef(null); + right.replaceWith(new StoreExpr(tmp, copy, right + .type())); + right = tmp; + } + + // If either the left expression or the right expression + // is a LocalExpr (meaning that it used to be a non-leaf + // expression and was replaced with a LocalExpr above), + // then prepend an assignment to the LocalExpr to the + // target block. + + if (left instanceof LocalExpr) { + LocalExpr tmp = (LocalExpr) left.clone(); + tmp.setDef(null); + Expr copy = (Expr) right.clone(); + copy.setDef(null); + Stmt insert = new ExprStmt(new StoreExpr(tmp, copy, + left.type())); + + target.tree().prependStmt(insert); + + } else if (right instanceof LocalExpr) { + LocalExpr tmp = (LocalExpr) right.clone(); + tmp.setDef(null); + Expr copy = (Expr) left.clone(); + copy.setDef(null); + Stmt insert = new ExprStmt(new StoreExpr(tmp, copy, + right.type())); + + target.tree().prependStmt(insert); + + } else { + Assert.isTrue(left instanceof ConstantExpr + && right instanceof ConstantExpr); + } + } + } + + } else if (last instanceof IfZeroStmt) { + IfZeroStmt stmt = (IfZeroStmt) last; + Block target = null; + + // Exclude targets which are mentioned more than once. + // This prevents: + // + // if (i == j) goto L + // else goto L + // L: + // i = j; + // + // Note: this shouldn't happen with IfStmts after critical. + // edges are removed. + // + if (stmt.trueTarget() == stmt.falseTarget()) { + continue; + } + + // Ignore all comparisons except for EQ and NE + if (stmt.comparison() == IfStmt.EQ) { + target = stmt.trueTarget(); + + } else if (stmt.comparison() == IfStmt.NE) { + target = stmt.falseTarget(); + } + + if (target != null) { + Expr left = stmt.expr(); + + if (!(left.type() instanceof ReferenceType)) { + // If left is not a leaf expression, make a new local + // variable and replace left with an assignment from + // left + // to the local variable. + + if (!(left instanceof LeafExpr)) { + int index = addLocal(left.type()); + LocalExpr tmp = new LocalExpr(index, left.type()); + Expr copy = (Expr) left.clone(); + copy.setDef(null); + left.replaceWith(new StoreExpr(tmp, copy, left + .type())); + left = tmp; + } + + // Value of the right hand side. 0 if left is an + // integer, + // null otherwise (left is a reference type). + Object value = null; + + Type type = left.type(); + + if (Assert.isIntegral(left.type())) { + value = new Integer(0); + + } else { + Assert.isTrue(left.type() instanceof ReferenceType); + } + + if (left instanceof LocalExpr) { + // Prepend the target block with an assignment + // from the + // value of the right hand side to the left + // expression. + + LocalExpr copy = (LocalExpr) left.clone(); + copy.setDef(null); + Stmt insert = new ExprStmt(new StoreExpr(copy, + new ConstantExpr(value, type), left.type())); + + target.tree().prependStmt(insert); + + } else { + Assert.isTrue(left instanceof ConstantExpr); + } + } + } + + } else if (last instanceof SwitchStmt) { + SwitchStmt stmt = (SwitchStmt) last; + + Expr index = stmt.index(); + + if (!(index instanceof LeafExpr)) { + // Replace index with a store into a new local variable + int local = addLocal(index.type()); + LocalExpr tmp = new LocalExpr(local, index.type()); + Expr copy = (Expr) index.clone(); + copy.setDef(null); + index.replaceWith(new StoreExpr(tmp, copy, index.type())); + index = tmp; + } + + if (index instanceof LocalExpr) { + Block[] targets = stmt.targets(); + int[] values = stmt.values(); + + // Exclude targets which are mentioned more than once. + // This prevents: + // + // switch (i) { + // case 0: + // case 1: + // case 2: + // i = 0; + // i = 1; + // i = 2; + // use(i); + // break; + // case 3: + // break; + // } + // + HashSet seen = new LinkedHashSet(); + + // Targets that are branched to more than once + HashSet duplicate = new LinkedHashSet(); + + for (int i = 0; i < targets.length; i++) { + if (seen.contains(targets[i])) { + duplicate.add(targets[i]); + } else { + seen.add(targets[i]); + } + } + + for (int i = 0; i < targets.length; i++) { + Block target = targets[i]; + + // Skip targets that can be branched to in multiple + // places + if (duplicate.contains(target)) { + continue; + } + + // Why do we split the edge? + splitEdge(block, targets[i]); + + // Make sure the edge was split. + Assert.isTrue(targets[i] != target); + + // Insert a store to the index on the new empty block. + LocalExpr copy = (LocalExpr) index.clone(); + copy.setDef(null); + Stmt insert = new ExprStmt(new StoreExpr(copy, + new ConstantExpr(new Integer(values[i]), index + .type()), index.type())); + + targets[i].tree().prependStmt(insert); + } + } + } + } + } + + /** + * Insert stores at the beginnings of protected regions to aid code + * generation for PhiCatchStmts. + */ + public void insertProtectedRegionStores() { + db(" Inserting protected region stores"); + + final HashSet tryPreds = new LinkedHashSet(); + + Iterator blocks = catchBlocks.iterator(); + + // Iterate over the blocks in this control flow graph. Build a + // set of predacessors of all protected blocks that themselves are + // not in the protected block. These blocks end with a jump into + // a protected region. + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Handler handler = (Handler) handlers.get(block); + + if (handler != null) { + HashSet p = new LinkedHashSet(); + + Iterator prots = handler.protectedBlocks().iterator(); + + while (prots.hasNext()) { + Block prot = (Block) prots.next(); + p.addAll(preds(prot)); + } + + p.removeAll(handler.protectedBlocks()); + + tryPreds.addAll(p); + } + } + + // Starting with the source block, + insertProtStores(srcBlock, tryPreds, new ResizeableArrayList()); + } + + /** + * Inserts copy statements into blocks whose successors lie in a protected + * region. + * + * @param block + * A block we are considering. + * @param tryPreds + * All blocks whose successor blocks lie in protected regions. + * @param defs + * Stores the expressions (LocalExprs) that define a given local + * variable (index into array) in block. Basically, it contains + * the LocalExpr that defines each local variable at a given + * block. + */ + private void insertProtStores(Block block, HashSet tryPreds, + final ResizeableArrayList defs) { + final Tree tree = block.tree(); + + // Visit all LocalExprs in block. Recall that LocalExpr + // represents a reference to a local variable. If the LocalExpr + // defines the variable, then added it to the defs array. defs is + // indexed by local variable. + tree.visitChildren(new TreeVisitor() { + public void visitLocalExpr(LocalExpr expr) { + if (expr.isDef()) { + int index = expr.index(); + + if (expr.type().getSize() == 2) { + defs.ensureSize(index + 2); + defs.set(index, expr); + defs.set(index + 1, null); + } else { + defs.ensureSize(index + 1); + defs.set(index, expr); + } + } + } + }); + + // If block ends in a jump to a block in a protected region, add + // statements that make a copy of each local variable. This is + // done to avoid redefining local variables used by the jump + // statement. I'm not too sure about all of this. + + if (tryPreds.contains(block)) { + // Examine all of the definitions of all the local variables + for (int i = 0; i < defs.size(); i++) { + LocalExpr expr = (LocalExpr) defs.get(i); + + if (expr != null) { + // Insert stores before the last stmt to ensure we don't + // redefine locals used by(?) the branch stmt. + final Stmt last = tree.lastStmt(); + + // Visit the Exprs in the last statement block. Remember + // that this statement ends in a jump to a protected + // block. + // Insert a store of the a copy of all Expr into a stack + // variable right before the jump. I think this saves + // all + // of the expressions in the jump statement to the + // stack. + // Why? I don't know. + + last.visitChildren(new TreeVisitor() { + public void visitExpr(Expr expr) { + StackExpr var = tree.newStack(expr.type()); + var.setValueNumber(expr.valueNumber()); + + Node p = expr.parent(); + expr.setParent(null); + p.visit(new ReplaceVisitor(expr, var)); + + var = (StackExpr) var.clone(); + var.setDef(null); + StoreExpr store = new StoreExpr(var, expr, expr + .type()); + store.setValueNumber(expr.valueNumber()); + + Stmt storeStmt = new ExprStmt(store); + storeStmt.setValueNumber(expr.valueNumber()); + + tree.addStmtBeforeJump(storeStmt); + } + + public void visitStackExpr(StackExpr expr) { + } + }); + + // Add assignment statements (StoreExpr) that store a + // copy + // of expr (a defining instance of LocalExpr) into + // itself. + + LocalExpr copy1 = (LocalExpr) expr.clone(); + LocalExpr copy2 = (LocalExpr) expr.clone(); + copy1.setDef(null); + copy2.setDef(null); + + StoreExpr store = new StoreExpr(copy1, copy2, expr.type()); + + tree.addStmtBeforeJump(new ExprStmt(store)); + } + } + } + + Iterator children = domChildren(block).iterator(); + + // Examine all of the blocks that block dominates. Note that + // local variables will have the same definitions as in block + // unless they are overriden in the child. + while (children.hasNext()) { + Block child = (Block) children.next(); + insertProtStores(child, tryPreds, new ResizeableArrayList(defs)); + } + } + + /** + * Removing unreachable Blocks means that there are Labels that are no + * longer label valid blocks (e.g. start basic blocks) in the CFG. However, + * we still want the Labels to point to something meaningful. So, we hoist + * them out of CFG and place them into the init block as LabelStmts. + */ + // DON"T KNOW maybe redirect to InstructionHandles at the start --Arrin + private void saveLabels() { + // Make sure any labels in the removed blocks are saved. + boolean save = false; + + Iterator iter = instructions.iterator(); + while (iter.hasNext()) { + InstructionHandle ih = (InstructionHandle) iter.next(); + + if (label(ih)) { + if (getNode(ih) == null) { + save = true; + } else { + save = false; + } + + if (save) { + unsetLabel(ih); + iniBlock.tree().addStmt(new LabelStmt(ih)); + } + } + } + } + + /** + * Removes a subroutine from this method. + * + * @param sub + * The subroutine to remove. + */ + public void removeSub(Subroutine sub) { + subroutines.remove(sub.entry()); + } + + /** + * Adds an edge between two nodes in this graph. + * + * @param src + * Node at which the edge originates. + * @param dst + * Node at which the edge terminates. + */ + public void addEdge(GraphNode src, GraphNode dst) { + if (DEBUG) { + System.out.println(" ADDING EDGE " + src + " -> " + dst); + } + + super.addEdge(src, dst); + } + + /** + * Removes an edge from the graph and performs the necessary cleanup. + * + * @param v + * Node at which edge to be removed originates. + * @param w + * Node at which edge to be removed terminates. + */ + public void removeEdge(GraphNode v, GraphNode w) { + Block src = (Block) v; + Block dst = (Block) w; + + if (DEBUG) { + System.out.println(" REMOVING EDGE " + src + " -> " + dst); + } + + super.removeEdge(src, dst); + + cleanupEdge(src, dst); + } + + /** + * Visit the tree starting at the destination node. + */ + private void cleanupEdge(final Block src, final Block dst) { + dst.visit(new TreeVisitor() { + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + Expr operand = stmt.operandAt(src); + + if (operand != null) { + operand.cleanup(); + + // Remove the operand associated with src + // from a PhiJoinStmt + stmt.setOperandAt(src, null); + } + } + + public void visitStmt(Stmt stmt) { + } + }); + } + + /** + * Returns a new Block with the next available Label. + */ + + public Block newBlock() { + Block block = new Block(this); + + if (DEBUG) { + System.out.println(" new block " + block); + } + + return block; + } + + /** + * Creates a new Block starting with the specified Label. The Block is added + * to this FlowGraph using its label as its key. + * + * @param label + * The new Block's Label + */ + Block newBlock(InstructionHandle label) { + Block block = new Block(label, this); + addNode(label, block); + + if (DEBUG) { + System.out.println(" new block " + block); + } + + return block; + } + + /** + * Uses classes DominatorTree and DominaceFrontier to calculate which blocks + * dominate which blocks. + * + * @see DominatorTree + * @see DominanceFrontier + */ + public void computeDominators() { + db(" Computing Dominators"); + + domEdgeModCount = edgeModCount; + + removeUnreachable(); + + // Forward + DominatorTree.buildTree(this, false); + DominanceFrontier.buildFrontier(this, false); + + // Reverse + DominatorTree.buildTree(this, true); + DominanceFrontier.buildFrontier(this, true); + } + + /** + * Locate the reducible loops. We get better results if we call + * splitIrreducibleLoops first to split reducible loop headers from + * irreducible loops. This method is based on the analyze_loops algorithm + * in: + * + * Paul Havlak, "Nesting of Reducible and Irreducible Loops", TOPLAS, 19(4): + * 557-567, July 1997. + */ + private void setBlockTypes() { + db(" Setting block types"); + + List blocks = preOrder(); + + // A block's predacessors that do not occur along back edges + Set[] nonBackPreds = new Set[blocks.size()]; + + // A block's predacessors that DO occur along back edges + Set[] backPreds = new Set[blocks.size()]; + + ListIterator iter = blocks.listIterator(); + + while (iter.hasNext()) { + Block w = (Block) iter.next(); + int wn = preOrderIndex(w); + + Set nonBack = new LinkedHashSet(); + nonBackPreds[wn] = nonBack; + + Set back = new LinkedHashSet(); + backPreds[wn] = back; + + w.setHeader(srcBlock); + w.setBlockType(Block.NON_HEADER); + + Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + Block v = (Block) preds.next(); + + // If w is an ancestor of v, (v,w) is a back edge. + if (isAncestorToDescendent(w, v)) { + back.add(v); + } else { + nonBack.add(v); + } + } + } + + srcBlock.setHeader(null); + + UnionFind uf = new UnionFind(blocks.size()); + + iter = blocks.listIterator(blocks.size()); + + while (iter.hasPrevious()) { + Block w = (Block) iter.previous(); + int wn = preOrderIndex(w); + + Set nonBack = nonBackPreds[wn]; + Set back = backPreds[wn]; + + Set body = new LinkedHashSet(); // The body of a loop + + Iterator preds = back.iterator(); + + // For each loop header, follow the back edges to construct the + // body of the loop + while (preds.hasNext()) { + Block v = (Block) preds.next(); + + if (v != w) { + int vn = preOrderIndex(v); + Block f = (Block) blocks.get(uf.find(vn)); + body.add(f); + + } else { + // Self loop + w.setBlockType(Block.REDUCIBLE); + } + } + + if (body.size() == 0) { + continue; + } + + // Initially assume the block is reducible + w.setBlockType(Block.REDUCIBLE); + + LinkedList worklist = new LinkedList(body); + + while (!worklist.isEmpty()) { + Block x = (Block) worklist.removeFirst(); + int xn = preOrderIndex(x); + + Iterator e = nonBackPreds[xn].iterator(); + + while (e.hasNext()) { + Block y = (Block) e.next(); // a block in the loop + int yn = preOrderIndex(y); + Block z = (Block) blocks.get(uf.find(yn)); // loop header + // of y + + if (!isAncestorToDescendent(w, z)) { + // If a block in the loop is not a descendent of the + // loop + // header, then there must be another entry path into + // the + // loop. Thus, the loop (and its header) are + // IRREDUCIBLE. + w.setBlockType(Block.IRREDUCIBLE); + nonBack.add(z); + + } else { + if (!body.contains(z) && z != w) { + // If we haven't seen z yet, add it to the + // worklist + body.add(z); + worklist.add(z); + } + } + } + } + + Iterator e = body.iterator(); + + // Merge all the blocks in the loop into the UnionFind set + while (e.hasNext()) { + Block x = (Block) e.next(); + int xn = preOrderIndex(x); + x.setHeader(w); + uf.union(xn, wn); + } + } + + // Say all loops containing jsrs or catch blocks are irreducible. + // This prevents some problems with peeling. + Iterator e = subroutines.values().iterator(); + + while (e.hasNext()) { + Subroutine sub = (Subroutine) e.next(); + + Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + + if (path[0].blockType() != Block.NON_HEADER) { + path[0].setBlockType(Block.IRREDUCIBLE); + } + + if (path[1].blockType() != Block.NON_HEADER) { + path[1].setBlockType(Block.IRREDUCIBLE); + } + + Block h; + + h = path[0].header(); + + if (h != null) { + h.setBlockType(Block.IRREDUCIBLE); + } + + h = path[1].header(); + + if (h != null) { + h.setBlockType(Block.IRREDUCIBLE); + } + } + } + + e = catchBlocks.iterator(); + + while (e.hasNext()) { + Block catchBlock = (Block) e.next(); + + if (catchBlock.blockType() != Block.NON_HEADER) { + catchBlock.setBlockType(Block.IRREDUCIBLE); + } + + Block h = catchBlock.header(); + + if (h != null) { + h.setBlockType(Block.IRREDUCIBLE); + } + } + } + + /** + * Ensure that no reducible back edge shares a destination with a + * irreducible back edge by splitting reducible loop headers from + * irredicible loops. This is based on the fix_loops algorithm in: + * + * Paul Havlak, "Nesting of Reducible and Irreducible Loops", TOPLAS, 19(4): + * 557-567, July 1997. + */ + + public void splitIrreducibleLoops() { + db(" Splitting irreducible loops"); + + List removeEdges = new LinkedList(); + + Iterator iter = nodes().iterator(); + + // Iterate over all the blocks in this cfg. If a block could be + // the header of a reducible loop (i.e. it is the target of a + // "reducible backedge", a backedge for which its destination + // dominates its source), the block is to be split. All + // "irreducible backedges" are placed in a list and will be used + // to insert an empty block so that the number of reducible loop + // headers is maximize. + while (iter.hasNext()) { + Block w = (Block) iter.next(); + + boolean hasReducibleBackIn = false; + Set otherIn = new LinkedHashSet(); + + Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + Block v = (Block) preds.next(); + + if (w.dominates(v)) { + // (v,w) is a reducible back edge. + hasReducibleBackIn = true; + + } else { + otherIn.add(v); + } + } + + if (hasReducibleBackIn && otherIn.size() > 1) { + Iterator e = otherIn.iterator(); + + while (e.hasNext()) { + Block v = (Block) e.next(); + removeEdges.add(new Block[] { v, w }); + } + } + } + + // Split the irreducible back edges. + iter = removeEdges.iterator(); + + while (iter.hasNext()) { + Block[] edge = (Block[]) iter.next(); + splitEdge(edge[0], edge[1]); + } + } + + /** + * Ensure that no reducible back edge shares a destination with another + * reducible back edge by splitting reducible loop headers. This makes loop + * nesting easier to detect since each loop has a unique header. + */ + + public void splitReducibleLoops() { + db(" Splitting reducible loops"); + + Map reducibleBackIn = new LinkedHashMap(); + + Stack stack = new Stack(); + + Iterator iter = nodes().iterator(); + + while (iter.hasNext()) { + Block w = (Block) iter.next(); + + Set edges = new LinkedHashSet(); // reducible back edges + + Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + Block v = (Block) preds.next(); + + if (w.dominates(v)) { + // (v,w) is a reducible back edge. + edges.add(v); + } + } + + // There are strange cases in which a handler block may be the + // target of a reducible backedge. Ignore it. + if (edges.size() > 1 && !handlers.containsKey(w)) { + stack.push(w); + reducibleBackIn.put(w, edges); + } + } + + while (!stack.isEmpty()) { + Block w = (Block) stack.pop(); + Set edges = (Set) reducibleBackIn.get(w); + + // Find the back predecessor with the lowest pre-order index. + Block min = null; + + Iterator preds = edges.iterator(); + + while (preds.hasNext()) { + Block v = (Block) preds.next(); + int vn = preOrderIndex(v); + + if (min == null || vn < preOrderIndex(min)) { + min = v; + } + } + + Assert.isTrue(min != null); + + Assert.isFalse(handlers.containsKey(w)); + Assert.isFalse(subroutines.containsKey(w)); + + // Split the edge (min, w) from w. + + // Create a new block immediately before the header. + // Block newBlock = newBlock(instructions.insert(w.label(), new + // NOP())); + + Block newBlock = newBlock(instructions.append(new NOP())); + trace.add(trace.indexOf(w), newBlock); + + Tree tree = new Tree(newBlock, min.tree().stack()); + newBlock.setTree(tree); + + InstructionHandle ih = instructions.append(newBlock.label(), + new GOTO(w.label())); + tree.addInstruction(ih.getInstruction()); + // If the header is a protected block, the new block must be + // also since code can be moved from the header up. + JumpStmt newJump = (JumpStmt) tree.lastStmt(); + + Iterator e = handlers.values().iterator(); + + while (e.hasNext()) { + Handler handler = (Handler) e.next(); + + if (handler.protectedBlocks().contains(w)) { + Assert.isTrue(succs(w).contains(handler.catchBlock())); + handler.protectedBlocks().add(newBlock); + addEdge(newBlock, handler.catchBlock()); + newJump.catchTargets().add(handler.catchBlock()); + } + } + + // Change all preds of the header, except min, to have an edge + // to the new block instead. + preds = new ImmutableIterator(preds(w)); + + while (preds.hasNext()) { + Block v = (Block) preds.next(); + + if (v != min) { + addEdge(v, newBlock); + removeEdge(v, w); + v.visit(new ReplaceTarget(w, newBlock)); + } + } + + // Add an edge from the new block to the header. + addEdge(newBlock, w); + + // If the new block has more than one back edge, push it on the + // stack to handle it next. + edges.remove(min); + + if (edges.size() > 1) { + stack.push(newBlock); + reducibleBackIn.put(newBlock, edges); + } + } + } + + /** + * Loop peeling is a process by which the first iteration of a loop is + * duplicated in the control flow graph. An code that has side effects (such + * as throwing an exception) will be tested in the first iteration. This + * allows us to make assumptions about the code in the second iteration. + * + * To detect loop nesting more easily we require that each loop header have + * at most one incoming back edge. + * + * For each loop, peel up to the last exit if: 1. There is more than one + * exit, or, 2. The last exit has a successor in the loop body (not the + * header). + */ + public void peelLoops(int level) { + if (DEBUG) { + System.out.println("Peeling loops"); + System.out.println(" loop tree = " + loopTree); + } + + // Find the blocks that have expressions that can throw exceptions + // and on which we can perform PRE. + final Set hoistable = new LinkedHashSet(); + + visit(new TreeVisitor() { + public void visitNode(Node node) { + if (!hoistable.contains(node.block())) { + node.visitChildren(this); + } + } + + public void visitCastExpr(CastExpr expr) { + if (expr.castType() instanceof ReferenceType) { + if (expr.expr() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + } + + visitNode(expr); + } + + public void visitArithExpr(ArithExpr expr) { + if (expr.operation() == ArithExpr.DIV + || expr.operation() == ArithExpr.REM) { + if (Assert.isIntegral(expr.type()) + && expr.left() instanceof LeafExpr + && expr.right() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + } + + visitNode(expr); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + if (expr.array() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + + visitNode(expr); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + if (expr.array() instanceof LeafExpr + && expr.index() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + + visitNode(expr); + } + + public void visitFieldExpr(FieldExpr expr) { + if (expr.object() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + + visitNode(expr); + } + }); + + // For each loop, from the innermost loop out, unroll the loop body + // up to the last block which exits the loop. + + // The (pre-order indices of the headers) loops that should be + // peeled + List peel = new ArrayList(loopTree.size()); + + // The header blocks of loops to be peeled + List headers = new ArrayList(loopTree.size()); + + // The outer loop of the loops to be peeled (i.e. parent in the + // loop tree) + List outer = new ArrayList(loopTree.size()); + + // All the loops in a tree in post-order + List loops = new ArrayList(loopTree.postOrder()); + + for (int i = 0; i < loops.size(); i++) { + LoopNode loop = (LoopNode) loops.get(i); + + // Don't peel irreducible loops or the outermost loop. + if (loopTree.preds(loop).size() > 0 + && loop.header.blockType() != Block.IRREDUCIBLE) { + + headers.add(loop.header); + peel.add(new Integer(i)); + + // Find the next outer loop. + LoopNode outerLoop = null; + + Iterator e = loopTree.preds(loop).iterator(); + Assert.isTrue(e.hasNext()); + + outerLoop = (LoopNode) e.next(); + Assert.isTrue(!e.hasNext()); + + int outerIndex = loops.indexOf(outerLoop); + Assert.isTrue(outerIndex != -1); + + outer.add(new Integer(outerIndex)); + } + } + + // The level of each loop to be peeled + int[] levels = new int[loops.size()]; + + // Replace the integer indicies in loops with the blocks in the + // loop to be peeled and note the level of each loop + for (int i = 0; i < loops.size(); i++) { + LoopNode loop = (LoopNode) loops.get(i); + loops.set(i, new ArrayList(loop.elements)); + levels[i] = loop.level; + maxLoopDepth = (loop.level > maxLoopDepth ? loop.level + : maxLoopDepth); + } + + LOOPS: + // Examine each loop that is a candidate for peeling. Peel it if + // we can. If we can't peel it, we might be able to invert it. + for (int i = 0; i < peel.size(); i++) { + // Index of loop header + Integer loopIndex = (Integer) peel.get(i); + Integer outerLoopIndex = (Integer) outer.get(i); + Block header = (Block) headers.get(i); + + Collection loop = (Collection) loops.get(loopIndex.intValue()); + Collection outerLoop = (Collection) loops.get(outerLoopIndex + .intValue()); + + // Remove any blocks from the loop that are not in this control + // flow graph. + loop.retainAll(nodes()); + + if (DEBUG) { + System.out.println(" loop = " + loop); + System.out.println(" outer = " + outerLoop); + } + + boolean canPeel = false; + boolean canInvert = false; + + // If we haven't exceeded the peeling level and the loop + // contains a block containing an expression that can be + // hoisted, then we should peel it. + + if (level != PEEL_NO_LOOPS) { + if (level == PEEL_ALL_LOOPS + || level >= levels[loopIndex.intValue()]) { + Iterator e = loop.iterator(); + + while (e.hasNext()) { + Block block = (Block) e.next(); + + if (hoistable.contains(block)) { + canPeel = true; + break; + } + } + } + } + + // If we can't peel it, we may still be able to invert it... + if (!canPeel) { + boolean hasExitSucc = false; + boolean hasLoopSucc = false; + + Iterator succs = succs(header).iterator(); + + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + if (!loop.contains(succ)) { + hasExitSucc = true; + } else if (succ != header) { + hasLoopSucc = true; + } + } + + // If the loop header has an edge to a block that is not in + // the loop, then it can be inverted. + canInvert = hasExitSucc && hasLoopSucc; + } + + // The blocks in the loop that are to be copied + Set copySet = new LinkedHashSet(); + + if (canPeel) { + // Find the blocks which have exits outside the loop. + Set exits = new LinkedHashSet(); + + // All blocks in the loop that may throw exceptions have an + // edge to outside the loop. + exits.addAll(hoistable); + exits.retainAll(loop); + + Iterator e = loop.iterator(); + + BLOCKS: while (e.hasNext()) { + Block block = (Block) e.next(); + + Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + if (!loop.contains(succ)) { + // If the successor of one of the blocks in the + // loop is + // itself not in the loop, then it is an exit + // block. + exits.add(block); + continue BLOCKS; + } + } + } + + ArrayList stack = new ArrayList(exits); + + e = exits.iterator(); + + // Add all "exit" blocks to the copy of the loop + while (e.hasNext()) { + Block block = (Block) e.next(); + copySet.add(block); + stack.add(block); + } + + // Copy all reachable blocks into the copy of the loop. Start + // with the exit blocks and work upwards. + while (!stack.isEmpty()) { + Block block = (Block) stack.remove(stack.size() - 1); + + Iterator preds = preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + + if (!copySet.contains(pred)) { + copySet.add(pred); + stack.add(pred); + } + } + } + + } else if (canInvert) { + // If all we're doing is inverting, just copy the loop header. + copySet.add(header); + + } else { + // If we can't invert or peel the loop, copy all the blocks + // to the outer loop and go to the next loop. + if (outerLoop != null) { + outerLoop.addAll(loop); + } + + // Consider the next loop to be peeled + continue LOOPS; + } + + // Maintain a mapping between a block in the loop and its copy + Map copies = new LinkedHashMap(); + + Iterator e = copySet.iterator(); + + // Go throught the blocks in the copy set and create a copy of + // each of them using copyBlock(). Make sure there are no + // duplicates. + while (e.hasNext()) { + Block block = (Block) e.next(); + + // Jeez, are we dealing with a finally block? + if (DEBUG) { + Stmt jump = block.tree().lastStmt(); + + if (jump instanceof JsrStmt) { + JsrStmt jsr = (JsrStmt) jump; + Assert.isTrue(copySet.contains(jsr.follow())); + Assert.isTrue(copySet.contains(jsr.sub().entry())); + } + } + + if (loop.contains(block)) { + Block copy = (Block) copies.get(block); + + if (copy == null) { + copy = copyBlock(block); + // addNode(copy.label(),copy); + copies.put(block, copy); + } + + // Add the copy to the list of hoistable blocks + if (hoistable.contains(block)) { + hoistable.add(copy); + } + } + } + + if (DEBUG) { + System.out.println(" copy = " + copies); + } + + int copyIndex = -1; + + e = preds(header).iterator(); + + // Determine the index into the trace to add the copy of the + // loop. Place the loop after the header's "latest" predacessor + // in the trace. + while (e.hasNext()) { + Block pred = (Block) e.next(); + + if (!header.dominates(pred)) { + int index = trace.indexOf(pred); + + if (copyIndex <= index) { + copyIndex = index + 1; + } + } + } + + if (copyIndex < 0) { + copyIndex = trace.indexOf(header); + } + + // Insert the copies into the trace just above the loop. + List copyTrace = new ResizeableArrayList(copies.size()); + + e = trace.iterator(); + + while (e.hasNext()) { + Block block = (Block) e.next(); + Block copy = (Block) copies.get(block); + + if (copy != null) { + copyTrace.add(copy); + } + } + + // Add copy of loop to trace + trace.addAll(copyIndex, copyTrace); + + // Edges to add to the control flow graph + List addEdges = new LinkedList(); + + // Edges to remove from the control flow graph + List removeEdges = new LinkedList(); + + // Fix up the edges for the block copies. + + // Add the edges within the peeled body and from the peeled body + // to the original body. + e = copies.entrySet().iterator(); + + while (e.hasNext()) { + Map.Entry pair = (Map.Entry) e.next(); + + Block block = (Block) pair.getKey(); + Block copy = (Block) pair.getValue(); + + Iterator h = handlers.values().iterator(); + + // The copy of the a protected block is also protected + while (h.hasNext()) { + Handler handler = (Handler) h.next(); + + if (handler.protectedBlocks().contains(block)) { + handler.protectedBlocks().add(copy); + } + } + + Iterator succs = succs(block).iterator(); + + // Make a list of edges to add to the control flow graph. + // Create edges within the copied loop so that it looks like + // the original loop. + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + Block succCopy = (Block) copies.get(succ); + + if (succ != header && succCopy != null) { + addEdges.add(new Block[] { copy, succCopy }); + copy.visit(new ReplaceTarget(succ, succCopy)); + } else { + addEdges.add(new Block[] { copy, succ }); + } + } + } + + // Add the edges from outside the loop to the peeled body. + // Remove the edges from outside the loop to the original body. + e = copies.entrySet().iterator(); + + while (e.hasNext()) { + Map.Entry pair = (Map.Entry) e.next(); + + Block block = (Block) pair.getKey(); + Block copy = (Block) pair.getValue(); + + Iterator preds = preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + + if (!loop.contains(pred)) { + addEdges.add(new Block[] { pred, copy }); + removeEdges.add(new Block[] { pred, block }); + pred.visit(new ReplaceTarget(block, copy)); + } + } + } + + e = addEdges.iterator(); + + // Add edges to the control flow graph + while (e.hasNext()) { + Block[] edge = (Block[]) e.next(); + addEdge(edge[0], edge[1]); + } + + e = removeEdges.iterator(); + + // Remove edges into the original (non-copied) loop + while (e.hasNext()) { + Block[] edge = (Block[]) e.next(); + Block v = edge[0]; + Block w = edge[1]; + + if (hasNode(v) && hasNode(w) && hasEdge(v, w)) { + removeEdge(v, w); + } + } + + // Copy all the blocks to the outer loop. + if (outerLoop != null) { + outerLoop.addAll(copies.values()); + outerLoop.addAll(loop); + } + } + + if (DEBUG) { + System.out.println("Begin after peeling:"); + System.out.println(this); + System.out.println("End after peeling"); + } + } + + /** + * Creates a copy of a block including its expression tree. + */ + private Block copyBlock(Block block) { + InstructionHandle ih = instructions.append(new NOP()); + Block copy = newBlock(ih); + copy.setLabel(ih); + + // Copy the stack from the end of the old block. + // But don't change it when instructions are added. + + Tree tree = new Tree(copy, block.tree().stack()); + copy.setTree(tree); + + // Fill the tree. + Iterator stmts = block.tree().stmts().iterator(); + + while (stmts.hasNext()) { + Stmt stmt = (Stmt) stmts.next(); + + if (stmt instanceof LabelStmt) { + continue; + } + + tree.addStmt((Stmt) stmt.clone()); + } + + return copy; + } + + /** + * Returns the Subroutine whose entry block is labeled by a given + * Label. + */ + public Subroutine labelSub(InstructionHandle label) { + return (Subroutine) subroutines.get(getNode(label)); + } + + /** + * Set the entry in the mapping between subroutine entry Blocks + * and the Subroutines that they begin. It also sets the + * Subroutine's entry block. + * + * @param sub + * The subroutine whose entry block is being set. + * @param entry + * The subroutine's entry Block. + * + * @see Subroutine#setEntry + */ + void setSubEntry(Subroutine sub, Block entry) { + if (sub.entry() != null) { + subroutines.remove(sub.entry()); + } + + sub.setEntry(entry); + subroutines.put(entry, sub); + } + + /** + * Returns all of the Subroutines in the method modeled by this + * FlowGraph. + */ + public Collection subroutines() { + return subroutines.values(); + } + + int file = 0; + + public void print(PrintStream out) { + print(new PrintWriter(out, true)); + } + + /** + * Prints the graph. + * + * @param out + * The writer to which to print. + */ + public void print(final PrintWriter out) { + String dateString = java.text.DateFormat.getDateInstance().format( + new Date()); + out.println("Print " + ++file + " at " + dateString + " " + + method.getReturnType() + " " + method.getName() + ":"); + + visit(new PrintVisitor(out)); + + if (PRINT_GRAPH) { + printGraph(); + } + } + + int next = 1; + + public void printGraph() { + try { + PrintStream out = new PrintStream(new FileOutputStream(method + .getName() + + "." + next++ + ".dot")); + printGraph(out); + + } catch (IOException e) { + } + + } + + public void print() { + try { + PrintStream out = new PrintStream(new FileOutputStream(method + .getName() + + "." + next++ + ".cfg")); + print(out); + + } catch (IOException e) { + } + + } + + /** + * Creates a graphical description of the CFG in the dot language. The name + * of the generated file is the name of the method modeled by this CFG + * followed by a number and the ".dot" postfix. For more information about + * dot and tools that use it see: + *

+ * http://www.research.att.com/sw/tools/graphviz/ + */ + public void printGraph(PrintStream out) { + printGraph(new PrintWriter(out, true)); + } + + public void printGraph(PrintWriter out) { + printGraph(out, "cfg"); + } + + public void printGraph(PrintWriter out, String name) { + method.getInstructionList().setPositions(); + out.println("digraph " + name + " {"); + out.println(" fontsize=8;"); + out.println(" ordering=out;"); + out.println(" center=1;"); + + visit(new PrintVisitor(out) { + public void println() { + super.print("\\n"); + } + + public void println(Object obj) { + super.print(obj); + super.print("\\n"); + } + + public void visitBlock(Block block) { + super + .print(" " + + block.label().getPosition() + + " [shape=box,fontname=\"Courier\",fontsize=6,label=\""); + block.visitChildren(this); + super.print("\"];\n"); + + Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + super.print(" " + block.label().getPosition() + " -> " + + succ.label().getPosition()); + + if (handlers.containsKey(succ)) { + super.print(" [style=dotted];\n"); + + } else { + super.print(" [style=solid];\n"); + } + } + } + }); + + out.println(" page=\"8.5,11\";"); + out.println("}"); + // out.close(); + } + + /** + * Visit each node (block) in this CFG in pre-order. + */ + public void visitChildren(TreeVisitor visitor) { + List list = preOrder(); + + if (!visitor.reverse()) { + ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + Block block = (Block) iter.next(); + block.visit(visitor); + } + + } else { + ListIterator iter = list.listIterator(list.size()); + + while (iter.hasPrevious()) { + Block block = (Block) iter.previous(); + block.visit(visitor); + } + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitFlowGraph(this); + } + + /** + * Returns the method editor for the method modeled by this graph. + */ + public MethodGen method() { + return method; + } + + /** + * Removes the critical edges (edges from a block with more than one + * successor to a block with more than one predecessor) from the graph. + * Critical edges can screw up code motion. + *

+ * For code generation, the block must be inserted after the predecessor if + * the successor is the default target. Throw successors and predecessors + * are copied from the successor block. + */ + + public void removeCriticalEdges() { + // The critical edges + List edges = new LinkedList(); + + Iterator blocks = nodes().iterator(); + + // Examine each block in this CFG. Blocks in subroutines, + // exception handlers, blocks with one or fewer predacessors, and + // the sink block are ignored. For all other blocks, dst, their + // predacessors are examined. If the predacessor, src, has more + // than one sucessor, then the edge from src to dst is a critical + // edge. A List of critical egdes is maintained. + while (blocks.hasNext()) { + Block dst = (Block) blocks.next(); + + // Skip edges to subroutine entries. + if (subroutines.containsKey(dst)) { + continue; + } + + // Skip edges from protected blocks to handlers. + if (handlers.containsKey(dst)) { + continue; + } + + if (preds(dst).size() <= 1) { + continue; + } + + if (dst == snkBlock) { + continue; + } + + Iterator preds = preds(dst).iterator(); + + while (preds.hasNext()) { + Block src = (Block) preds.next(); + + if (succs(src).size() <= 1) { + continue; + } + + // The edge src->dst is a critical edge. Plop a new + // block on the edge. + + edges.add(new Block[] { src, dst }); + } + } + + Iterator e = edges.iterator(); + + // Remove the critical edges from this CFG. Call splitEdge to add + // a block between the source and destination blocks of the + // critical edges so that it is no longer critical. + while (e.hasNext()) { + Block[] edge = (Block[]) e.next(); + Block v = edge[0]; + Block w = edge[1]; + + if (hasEdge(v, w)) { + if (FlowGraph.DEBUG) { + System.out.println("removing critical edge from " + v + + " to " + w); + } + + splitEdge(v, w); + + Assert.isFalse(hasEdge(v, w)); + } + } + } + + /** + * Splits an edge by inserting an new block between a source and a + * destination block. The new block consists of a goto to the destination + * block. However, later optimizations may move code from the destination + * block into the new block. Thus, if the destination block is a proteced + * block, the new block must also be a protected block. + */ + private void splitEdge(Block src, Block dst) { + // This shouldn't happen since + // (1) edges from the source are either source->init, or + // source->catch. Edges with catch blocks are already split. + // (2) edges to the sink are always unconditional jumps. + // + Assert.isFalse(src == srcBlock || dst == snkBlock, + "Can't split an edge from the source or to the sink"); + + // Don't split exception edges + if (handlers.containsKey(dst)) { + if (FlowGraph.DEBUG) { + System.out.println("not removing exception edge " + src + + " -> " + dst); + } + + return; + } + + Block newBlock = newBlock(); + + // Insert in the trace before the dst. + trace.add(trace.indexOf(dst), newBlock); + // Shouldn't matter greatly but if we have cause to we want insert stuff + // between the label and a goto so the goto shouldn't also be a label. + InstructionHandle ih = instructions.append(new NOP()); + newBlock.setLabel(ih); // want it to have a Label ASAP + ih = instructions.append(new GOTO(dst.label())); + Tree tree = new Tree(newBlock, src.tree().stack()); + newBlock.setTree(tree); + tree.addInstruction(ih.getInstruction()); + addNode(newBlock.label(), newBlock); + if (FlowGraph.DEBUG) { + System.out.println("add edge " + src + " -> " + newBlock); + System.out.println("add edge " + newBlock + " -> " + dst); + System.out.println("remove edge " + src + " -> " + dst); + } + + src.visit(new ReplaceTarget(dst, newBlock)); + + addEdge(src, newBlock); + addEdge(newBlock, dst); + removeEdge(src, dst); + + Assert.isTrue(hasEdge(src, newBlock)); + Assert.isTrue(hasEdge(newBlock, dst)); + Assert.isFalse(hasEdge(src, dst)); + + // If the dst is a protected block, the new block must be + // also since code can be moved from the dst up. + JumpStmt newJump = (JumpStmt) newBlock.tree().lastStmt(); + + Iterator e = handlers.values().iterator(); + + while (e.hasNext()) { + Handler handler = (Handler) e.next(); + + if (handler.protectedBlocks().contains(dst)) { + Assert.isTrue(succs(dst).contains(handler.catchBlock())); + handler.protectedBlocks().add(newBlock); + addEdge(newBlock, handler.catchBlock()); + newJump.catchTargets().add(handler.catchBlock()); + } + } + } + + /** + * Finds any blocks in the CFG that are both the entry block of a subroutine + * and the return target of (another) subroutine. + *

+ * The Subroutine's in the cfg are examined. If a block is encountered that + * is both a subroutine entry block and the target of a subroutine return, + * then we have to make two new blocks: a new target block and a new entry + * block. The edges have to be adjusted accordingly. + */ + + public void splitPhiBlocks() { + // Make sure a block is not more than one of: a catch block, + // a sub entry block, a sub return target. + // Otherwise, more than one SSA phi could be placed at the block. + // + // Since catch blocks and return targets are mutually exclusive + // and since catch blocks and sub entries are mutually exclusive, + // we need only check if the block is both an entry block and a + // return target. Actually, I don't think this can possibly + // happen, but do it just to be sure. + // + // Note that a phi can also be placed at the block if it has + // more than one predecessor, but this condition and the others + // are mutually exclusive since catch blocks and sub entries have + // only the single source predecessor and return targets have + // only the caller block as its predecessor. + // + Iterator entries = subroutines.values().iterator(); + + ENTRIES: while (entries.hasNext()) { + Subroutine entrySub = (Subroutine) entries.next(); + + // An entry block of a subroutine + Block block = entrySub.entry(); + + Subroutine returnSub = null; + + // A block that calls a subroutine that is followed by a block + // (the return target of the subroutine) that also starts a + // subroutine. + Block returnSubCaller = null; + + Iterator returns = subroutines.values().iterator(); + + RETURNS: while (returns.hasNext()) { + returnSub = (Subroutine) returns.next(); + + if (returnSub == entrySub) { + continue; + } + + Iterator paths = returnSub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + + // If the block to which returnSub returns is also the + // entry + // block of entrySub, then note the caller of returnSub + // as the + // returnSubCaller. + if (block == path[1]) { + returnSubCaller = path[0]; + break RETURNS; + } + } + } + + if (returnSubCaller == null) { + continue ENTRIES; + } + + if (DEBUG) { + System.out.println("" + block + + " is both an entry and a return target"); + } + + // Create new blocks to be the new sub entry block and the new + // return target. + // + // Use the returning subroutine's exit block to get the state + // of the operand stack. + // + int traceIndex = trace.indexOf(block); + + Tree tree; + + Block newEntry = newBlock(); + + // Insert in the trace before the block. + trace.add(traceIndex, newEntry); + + tree = new Tree(newEntry, returnSub.exit().tree().stack()); + newEntry.setTree(tree); + + InstructionHandle ih = instructions.insert(block.label(), new GOTO( + block.label())); + newEntry.setLabel(ih); // want it to have a Label ASAP + tree.addInstruction(ih.getInstruction()); + + addEdge(newEntry, block); + + Iterator paths = entrySub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + removeEdge(path[0], block); + addEdge(path[0], newEntry); + path[0].visit(new ReplaceTarget(block, newEntry)); + } + + setSubEntry(entrySub, newEntry); + + Block newTarget = newBlock(); + + // Insert in the trace immediately after the jsr block. + trace.add(traceIndex, newTarget); + + tree = new Tree(newTarget, returnSub.exit().tree().stack()); + newTarget.setTree(tree); + ih = instructions.append(new NOP()); + instructions.append(new GOTO(block.label())); + newTarget.setLabel(ih); // want it to have a Label ASAP + tree.addInstruction(ih.getInstruction()); + + returnSub.exit().visit(new ReplaceTarget(block, newTarget)); + ((JsrStmt) returnSubCaller.tree().lastStmt()).setFollow(newTarget); + + addEdge(newTarget, block); + addEdge(returnSub.exit(), newTarget); + removeEdge(returnSub.exit(), block); + + JumpStmt entryJump = (JumpStmt) newEntry.tree().lastStmt(); + JumpStmt targetJump = (JumpStmt) newTarget.tree().lastStmt(); + + Iterator e = handlers.values().iterator(); + + // If block itself is a protected block (man, this block has + // problems), add egdes from the newEntry and newTarget blocks + // to the handlers for block. + while (e.hasNext()) { + Handler handler = (Handler) e.next(); + + if (handler.protectedBlocks().contains(block)) { + Assert.isTrue(succs(block).contains(handler.catchBlock())); + + handler.protectedBlocks().add(newEntry); + addEdge(newEntry, handler.catchBlock()); + entryJump.catchTargets().add(handler.catchBlock()); + + handler.protectedBlocks().add(newTarget); + addEdge(newTarget, handler.catchBlock()); + targetJump.catchTargets().add(handler.catchBlock()); + } + } + } + } + + /** + * Builds the expressions trees for the "special" blocks (source, sink, and + * init blocks). Empty expressions trees are built for the source and sink + * blocks. The init block's expression tree contains code that initializes + * the method's parameters (represented as local variables). + *

+ * + * @param catchBodies + * + */ + + private void buildSpecialTrees(Map catchBodies) { + Tree tree; + + tree = new Tree(srcBlock, new OperandStack()); + srcBlock.setTree(tree); + + tree = new Tree(snkBlock, new OperandStack()); + snkBlock.setTree(tree); + + tree = new Tree(iniBlock, new OperandStack()); + iniBlock.setTree(tree); + + if (instructions.size() > 0) { + tree.initLocals(methodParams(method)); + + InstructionHandle ih = instructions.insert(iniBlock.label() + .getNext(), new GOTO(instructions.getStart())); + tree.addInstruction(ih.getInstruction()); + + // unfortunately this is a bit later + if (catchBodies != null) { + addHandlerEdges(iniBlock, catchBodies, null, + new LinkedHashSet()); + } + } + } + + /** + * If a block may throws an exception (i.e. it is in a protected region), + * there must be an edge in the control flow graph from that block to the + * block that begins the exception handler. This method adds that edge. + *

+ * We iterate over all of the Handler objects created for this FlowGraph. If + * the block of interest lies in the protected region of the Handler, make + * note of this fact and add an edge between the block and the first block + * of the exception handler. Generate the expression tree for the exception + * handler, if necessary. + *

+ * Recursively call addHandlerEdges for the exception handler to accomodate + * exception handlers within exception handlers. + * + * @param block + * The "block of interest" (i.e. may throw an exception) + * @param catchBodies + * Maps "catch blocks" (first block of exception handler) to + * "catch bodies" (block that begins the actual work of the + * exception handler). + * @param sub + * The current Subroutine we're in (needed for + * buildTreeForBlock). + */ + private void addHandlerEdges(Block block, Map catchBodies, Subroutine sub, + Set visited) { + if (visited.contains(block)) + return; + + visited.add(block); + + Tree tree = block.tree(); + + Assert.isTrue(tree != null); + + Iterator hiter = handlers.values().iterator(); + + // Iterate over every Handler object created for this FlowGraph + while (hiter.hasNext()) { + Handler handler = (Handler) hiter.next(); + + boolean prot = false; + + // Determine whether or not the block of interest lies within + // the Handler's protected region + if (handler.protectedBlocks().contains(block)) { + prot = true; + + } else { + Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + if (handler.protectedBlocks().contains(succ)) { + prot = true; + break; + } + } + } + + // If the block of interest lies in a protected region, add an + // edge in this CFG from the block to the Handler's "catch + // block" + // (i.e. first block in Handler). Also examine the JumpStmt that + // ends the block of interest and add the catch block to its + // list + // of catch targets. + // + // Note that we do not want the init block to be protected. + // This may happen if the first block in the CFG is protected. + // + // Next, obtain the "catch body" block (contains the real code) + // of the method. If no expression tree has been constructed for + // it, create a new OperandStack containing only the exception + // object and build a new tree for it. + // + // Finally, recursively add the handler edges for the first + // block + // of the exception handler. + if (prot) {// && block != iniBlock) { + Block catchBlock = handler.catchBlock(); + if (DEBUG) + System.out.println("Last Stmt of : " + block.label() + + " is " + tree.lastStmt()); + JumpStmt jump = (JumpStmt) tree.lastStmt(); + if ((DEBUG) && (jump == null)) + System.out.println("Jump is null"); + if ((DEBUG) && (catchBlock == null)) + System.out.println("catchBlock is null"); + jump.catchTargets().add(catchBlock); + addEdge(block, catchBlock); + + // Build the tree for the exception handler body. + // We must have already added the edge from the catch block + // to the catch body. + + Block catchBody = (Block) catchBodies.get(catchBlock); + Assert.isTrue(catchBody != null); + + if (catchBody.tree() == null) { + OperandStack s = new OperandStack(); + s.push(new StackExpr(0, Type.THROWABLE)); + + buildTreeForBlock(catchBody, s, sub, catchBodies); + } + + addHandlerEdges(catchBlock, catchBodies, sub, visited); + } + } + } + + /** + * Dave sez: Builds the expression tree for a basic block and all blocks + * reachable from that block. Basically, the block's code (Instructions and + * Labels) are iterated over. The instructions are added to the tree with + * calls to Tree#addInstruction. If an instruction invovles a change of + * control flow (e.g. jsr, jump, switch), add an edge in the control flow + * graph between the appropriate blocks. After all that is done, call + * addHandlerEdges to add edges between blocks that may throw exceptions and + * the blocks that handle those exceptions + * + * Nate sez: Visit a block other than source or catch. Since blocks are + * visited depth-first, one predecessor was already visited, get the operand + * stack state at the end of the predecessor block and use it as the initial + * operand stack state for this block. We assume the class file passed + * verification so which predecessor used shouldn't matter. + * + * @param block + * The block for which to generate an expression tree. + * @param stack + * The operand stack before the block is executed. + * @param sub + * The current Subroutine. + * @param catchBodies + * A mapping between "catch blocks" and "catch bodies" + */ + private void buildTreeForBlock(Block block, OperandStack stack, + Subroutine sub, Map catchBodies) { + if (block.tree() != null) { + return; + } + + Tree tree = new Tree(block, stack); + block.setTree(tree); + + InstructionHandle start = block.label(); + + InstructionHandle handle = start; + + CODE: + + // Iterate over the code in the method... + while (handle != null) { + + Instruction inst = handle.getInstruction(); + + Block target; // The target of a jump + Block next = null; // The Block following a jump + // For jump instructions, look for the next Block + + if (inst instanceof JsrInstruction || inst instanceof IfInstruction) { + // || inst instanceof Select) { + int save = 0; + InstructionHandle handle2 = handle; + while (handle2 != null) { + handle2 = handle2.getNext(); + save++; + + if (label(handle2)) { + next = (Block) getNode(handle2); + + while (save-- > 0) { + handle2 = handle2.getPrev(); + } + break; + } else { + Instruction inst2 = handle2.getInstruction(); + throw new RuntimeException(inst + + " not followed by a label: " + inst2 + " (" + + inst2.getClass() + ")"); + } + } + } + + if (inst instanceof ASTORE) { + // We need the current subroutine in case this is a + // returnAdress store. + tree.addInstruction((ASTORE) inst, sub); + + } else if (inst instanceof RET) { + sub.setExit(block); + tree.addInstruction((RET) inst, sub); + + Iterator paths = sub.paths().iterator(); + + // Add edges from the exit Block of the Subroutine to the + // Block that begins with the Subroutine's return address + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + addEdge(block, path[1]); + } + + break CODE; + + } else if (inst instanceof ATHROW + || inst instanceof ReturnInstruction) { + tree.addInstruction(inst); + addEdge(block, snkBlock); + break CODE; + + } else if (inst instanceof JsrInstruction) { + Assert.isTrue(next != null, inst + " not followed by a block"); + JsrInstruction jsr = (JsrInstruction) inst; + tree.addInstruction((JsrInstruction) jsr, next); + + InstructionHandle label = jsr.getTarget(); + + target = (Block) getNode(label); + Assert.isTrue(target != null, inst + " target not found"); + + Subroutine nextSub = labelSub(label); + setSubEntry(nextSub, target); + + buildTreeForBlock(target, tree.stack(), nextSub, catchBodies); + addEdge(block, target); + + if (nextSub.exit() != null) { + buildTreeForBlock(next, nextSub.exit().tree().stack(), sub, + catchBodies); + addEdge(nextSub.exit(), next); + } + + break CODE; + + } else if (inst instanceof GotoInstruction) { + GotoInstruction go2 = (GotoInstruction) inst; + tree.addInstruction(go2); + + InstructionHandle ih = go2.getTarget(); + + target = (Block) getNode(ih); + Assert.isTrue(target != null, inst + " target not found"); + + addEdge(block, target); + + buildTreeForBlock(target, tree.stack(), sub, catchBodies); + + break CODE; + + } else if (inst instanceof IfInstruction) { + IfInstruction ifInst = (IfInstruction) inst; + Assert.isTrue(next != null, inst + " not followed by a block"); + + tree.addInstruction(ifInst, next); + + InstructionHandle ih = ifInst.getTarget(); + + target = (Block) getNode(ih); + Assert.isTrue(target != null, inst + " target not found"); + + addEdge(block, target); + buildTreeForBlock(target, tree.stack(), sub, catchBodies); + + addEdge(block, next); + buildTreeForBlock(next, tree.stack(), sub, catchBodies); + + break CODE; + + } else if (inst instanceof Select) { + Select sw = (Select) inst; + tree.addInstruction(sw); + + // Do the same for the default target as the other targets + target = (Block) getNode(sw.getTarget()); + + addEdge(block, target); + + buildTreeForBlock(target, tree.stack(), sub, catchBodies); + + for (int j = 0; j < sw.getTargets().length; j++) { + target = (Block) getNode(sw.getTargets()[j]); + + addEdge(block, target); + + buildTreeForBlock(target, tree.stack(), sub, catchBodies); + } + + break CODE; + + } else { + if (label(handle)) { + if (!handle.equals(start)) { + InstructionHandle ih = instructions.insert(handle, + new GOTO(handle)); + tree.addInstruction(ih.getInstruction()); + + Block next2 = (Block) getNode(handle); + + Assert.isTrue(next2 != null, "Block for " + handle + + " not found"); + + addEdge(block, next2); + buildTreeForBlock(next2, tree.stack(), sub, catchBodies); + break CODE; + } + } + tree.addInstruction(inst); + } + + handle = handle.getNext(); + } + + addHandlerEdges(block, catchBodies, sub, new LinkedHashSet()); + } + + /** + * Returns an ArrayList of the parameters of a method, including the + * receiver (non-static methods only). + * + * @param method + * The method. + */ + private ArrayList methodParams(MethodGen method) { + ArrayList locals = new ArrayList(); + + int index = 0; + + if (!method.isStatic()) { + // Add the this pointer to the locals. + Type type = new ObjectType(method.getClassName()); + locals.add(new LocalExpr(index, type)); + index++; + } + + Type[] paramTypes = method.getArgumentTypes(); + + for (int i = 0; i < paramTypes.length; i++) { + Assert.isNotNull(paramTypes[i], "Argument " + i + " of method " + + method.getName() + " of class " + method.getClassName() + + " has a null type."); + locals.add(new LocalExpr(index, paramTypes[i])); + // locals.add(new LocalExpr(index, method.getArgumentType(i))); + + index++; + } + + return locals; + } + + /** + * Returns the basic blocks contained in this CFG in trace order. Trace + * order implies that basic blocks that end with a conditional jump are + * followed by their false branch and, where possible, that blocks that end + * in an unconditional jump are followed by the block that is the target of + * the unconditional branch. + *

+ * The trace does not contain the source and the sink blocks. + * + * @return The basic Blocks in this CFG. + */ + public List trace() { + // The trace must include everything but the source and sink. + Assert.isTrue(trace.size() == size() - 2, "trace contains " + + trace.size() + " " + trace + " blocks, not " + (size() - 2) + + " " + nodes()); + return trace; + } + + /** + * Commit changes back to the method editor. + */ + + public void commit() { + method.removeExceptionHandlers(); + method.setInstructionList(new InstructionList()); + method.setMaxLocals(0); + method.setMaxStack(0); + + CodeGenerator codegen = new CodeGenerator(method); + visit(codegen); + + // Add all the handlers back in the same order we got them. + // This ensures that the correct catch clause will be called + // when an exception is thrown. + Iterator iter = catchBlocks.iterator(); + + while (iter.hasNext()) { + Block catchBlock = (Block) iter.next(); + + Handler handler = (Handler) handlers.get(catchBlock); + Assert.isTrue(handler != null); + + Type type = handler.catchType(); + + if (type == Type.NULL) { + type = null; + } + + Block begin = null; + + Iterator blocks = trace().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + if (handler.protectedBlocks().contains(block)) { + if (begin == null) { + begin = block; + } + } else if (begin != null) { + method.addExceptionHandler(begin.label(), block.label(), + catchBlock.label(), (ObjectType) type); + + begin = null; + } + } + } + } + + /** + * Returns the "Enter" block of this CFG. That is, the block through which + * all paths enter. + */ + public Block source() { + return srcBlock; + } + + /** + * Returns the initialization block. + * + */ + public Block init() { + return iniBlock; + } + + /** + * Returns the sink block. That is, the block through which all paths exit. + */ + public Block sink() { + return snkBlock; + } + + /** + * Returns the iterated dominance frontiers for several basic blocks. + * + * @see Block#domFrontier + */ + public Collection iteratedDomFrontier(Collection blocks) { + return idf(blocks, false); + } + + /** + * Returns the iterated postdominance frontier for several basic blocks. + * + * @see Block#pdomFrontier + */ + public Collection iteratedPdomFrontier(Collection blocks) { + return idf(blocks, true); + } + + /** + * Returns the iterated dominance frontier (DF+) for a given set of blocks. + *

+ * The iterated dominance frontier for a set of nodes is defined to be the + * union of the dominance frontiers of all the nodes in the set. + *

+ * The iterated dominance frontier is particularly useful because the DF+ of + * an assignment node for a variable (expression) specifies the nodes at + * which phi-functions (PHI-functions) need to be inserted. + * + * @param blocks + * The + * @param reverse + * Do we find the reverse (i.e. postdominance) dominance + * frontier. + * + * @see SSAPRE#placePhis + */ + private Collection idf(Collection blocks, boolean reverse) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + HashSet idf = new LinkedHashSet(); + + HashSet inWorklist = new LinkedHashSet(blocks); + LinkedList worklist = new LinkedList(inWorklist); + + while (!worklist.isEmpty()) { + Block block = (Block) worklist.removeFirst(); + + Collection df; + + if (!reverse) { + df = block.domFrontier(); + } else { + df = block.pdomFrontier(); + } + + Iterator iter = df.iterator(); + + while (iter.hasNext()) { + Block dfBlock = (Block) iter.next(); + idf.add(dfBlock); + + if (inWorklist.add(dfBlock)) { + worklist.add(dfBlock); + } + } + } + + return idf; + } + + /** + * @return A Collection containing the root(s) of this FlowGraph. In this + * case there is only one root, so the Collection only contains the + * source block. + */ + public Collection roots() { + return new AbstractCollection() { + public int size() { + return 1; + } + + public boolean contains(Object obj) { + return obj == srcBlock; + } + + public Iterator iterator() { + return new Iterator() { + Object next = srcBlock; + + public boolean hasNext() { + return next != null; + } + + public Object next() { + Object n = next; + next = null; + return n; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + /** + * @return A Collection containing only the sink block. + * + * @see #roots + */ + public Collection reverseRoots() { + return new AbstractCollection() { + public int size() { + return 1; + } + + public boolean contains(Object obj) { + return obj == snkBlock; + } + + public Iterator iterator() { + return new Iterator() { + Object next = snkBlock; + + public boolean hasNext() { + return next != null; + } + + public Object next() { + Object n = next; + next = null; + return n; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + /** + * Removes a node (a Block) from the graph. + * + * @param key + * Block to remove + */ + public void removeNode(Object key) { + Block block = (Block) getNode(key); + removeBlock(block); + } + + /** + * Returns A Map mapping the first block in an exception handler to its + * Handler object. + * + * @see Handler + */ + public Map handlersMap() { + return handlers; + } + + /** + * Returns all of the Handler objects in this CFG. + */ + public Collection handlers() { + return handlers.values(); + } + + /** + * Returns theBlocks in this CFG that begin exception handlers. + */ + public List catchBlocks() { + return catchBlocks; + } + + private void removeBlock(Block block) { + trace.remove(block); + subroutines.remove(block); + catchBlocks.remove(block); + handlers.remove(block); + + // edgeModCount is incremented by super.removeNode(). + // Dominators will be recomputed automatically if needed, so just + // clear the pointers to let the GC work. + + block.setDomParent(null); + block.setPdomParent(null); + block.domChildren().clear(); + block.pdomChildren().clear(); + block.domFrontier().clear(); + block.pdomFrontier().clear(); + + Iterator iter = handlers.values().iterator(); + + while (iter.hasNext()) { + Handler handler = (Handler) iter.next(); + handler.protectedBlocks().remove(block); + } + + iter = subroutines.values().iterator(); + + while (iter.hasNext()) { + Subroutine sub = (Subroutine) iter.next(); + sub.removePathsContaining(block); + + if (sub.exit() == block) { + sub.setExit(null); + } + } + + if (block.tree() != null) { + iter = block.tree().stmts().iterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + + if (s instanceof LabelStmt) { + InstructionHandle label = ((LabelStmt) s).label(); + unsetLabel(label); + iniBlock.tree().addStmt(new LabelStmt(label)); + } + + s.cleanup(); + } + } + + super.removeNode(block.label()); + } + + /** + * Returns the blocks that a given block dominates. + */ + public Collection domChildren(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.domChildren(); + } + + /** + * Returns the Block that dominates a given block. + */ + public Block domParent(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.domParent(); + } + + /** + * Returns the type of a given block. A block's type is one of + * Block.NON_HEADER, Block.IRREDUCIBLE, or + * Block.REDUCIBLE. + */ + public int blockType(Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + return block.blockType(); + } + + /** + * Returns the depth of the loop in which a block is contained. The block + * must be contained in a loop. The procedure has depth 0. A loop (while, + * for, etc.) at the procedure level has depth 1. Depth increases as loops + * are nested. + * + * @param block + * A block whose depth we are interested in. + * + * @see #loopLevel + */ + public int loopDepth(Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + if (block == srcBlock || block.blockType() != Block.NON_HEADER) { + LoopNode loop = (LoopNode) loopTree.getNode(block); + Assert.isTrue(loop != null, "no loop for " + block); + return loop.depth; + } + + if (block.header() != null) { + LoopNode loop = (LoopNode) loopTree.getNode(block.header()); + Assert.isTrue(loop != null, "no loop for " + block.header()); + return loop.depth; + } + + throw new RuntimeException(); + } + + /** + * Returns the level of the loop containing a given block. The innermost + * loops have level 0. The level increases as you go outward to higher loop + * nestings. For any given loop, the level is the maximum possible. + *

+ * + *

+     *     procedure()
+     *     {
+     *       // Depth 0, Level 2 (max possible)
+     *       while()
+     *       {
+     *         // Depth 1, Level 1
+     *         while()
+     *         {
+     *           // Depth 2, Level 0
+     *         }
+     *       }
+     *       while()
+     *       {
+     *         // Depth 1, Level 0
+     *       }
+     *     }
+     * 
+ * + * @param block + * A block whose loop level we want to know. This block must be + * contained in a loop. + */ + public int loopLevel(Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + if (block == srcBlock || block.blockType() != Block.NON_HEADER) { + LoopNode loop = (LoopNode) loopTree.getNode(block); + Assert.isTrue(loop != null, "no loop for " + block); + return loop.level; + } + + if (block.header() != null) { + LoopNode loop = (LoopNode) loopTree.getNode(block.header()); + Assert.isTrue(loop != null, "no loop for " + block.header()); + return loop.level; + } + + throw new RuntimeException(); + } + + /** + * Returns the loop header of the loop containing a given block. The loop + * header is the block that dominates all of the blocks in the loop. + */ + public Block loopHeader(Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + return block.header(); + } + + /** + * Returns the blocks in the flow graph sorted in pre-order. + */ + public List preOrder() { + return super.preOrder(); + } + + /** + * Returns the blocks in the flow graph sorted in post-order. + */ + public List postOrder() { + return super.postOrder(); + } + + /** + * Returns the postdominator children of a given block. + * + * @see Block#pdomChildren + */ + public Collection pdomChildren(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.pdomChildren(); + } + + /** + * Returns the postdominator parent of a given block. + * + * @see Block#pdomParent + */ + public Block pdomParent(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.pdomParent(); + } + + /** + * Returns the dominance frontier of a given block. + * + * @see Block#domFrontier + */ + public Collection domFrontier(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.domFrontier(); + } + + /** + * Returns the postdominance frontier of a given block. + * + * @see Block#pdomFrontier + */ + public Collection pdomFrontier(Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.pdomFrontier(); + } + + /** + * A LoopNode is a node in the loop tree. The loop tree is represents the + * nesting of loops in the method being modeled in this CFG. + * + * @see loopDepth + * @see loopLevel + */ + class LoopNode extends GraphNode { + Block header; + + int depth; + + int level; + + Set elements; + + public LoopNode(Block header) { + this.header = header; + this.depth = 1; + this.level = 1; + this.elements = new LinkedHashSet(); + this.elements.add(header); + } + + public String toString() { + return "level=" + level + " depth=" + depth + " header=" + header + + " " + elements; + } + } + + /** + * Returns a brief textual description of this FlowGraph, namely + * the name of the method it represents. + */ + public String toString() { + return ("CFG for " + method.getName()); + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/Handler.java b/src/edu/purdue/cs/bloat/cfg/Handler.java new file mode 100644 index 0000000..4bc2f7c --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/Handler.java @@ -0,0 +1,97 @@ +/* + * Class: Handler + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import org.apache.bcel.generic.Type; +import java.util.*; + +/** + * Handler represents a try-catch block. It containes a set of + * protected Blocks (the "try" blocks), a catch Block, + * and the Type of exception that is caught by the catch block. + * + * @see Block + * @see edu.purdue.cs.bloat.reflect.Catch + * @see edu.purdue.cs.bloat.editor.TryCatch + */ +public class Handler { + Set protectedBlocks; + + Block catchBlock; + + final Type type; + + /** + * Constructor. + * + * @param catchBlock + * The block of code that handles an exception + * @param type + * The type of exception that is thrown + */ + public Handler(Block catchBlock, Type type) { + this.protectedBlocks = new LinkedHashSet(); + this.catchBlock = catchBlock; + this.type = type; + } + + /** + * Returns a Collection of the "try" blocks. + */ + public Collection protectedBlocks() { + return protectedBlocks; + } + + public void setCatchBlock(Block block) { + catchBlock = block; + } + + public Block catchBlock() { + return catchBlock; + } + + public Type catchType() { + return type; + } + + public String toString() { + return "try -> catch (" + type + ") " + catchBlock; + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/Makefile b/src/edu/purdue/cs/bloat/cfg/Makefile new file mode 100644 index 0000000..15fe228 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/Makefile @@ -0,0 +1,31 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + Block.class\ + DominanceFrontier.class\ + DominatorTree.class\ + FlowGraph.class\ + ReplaceTarget.class\ + Subroutine.class\ + VerifyCFG.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/cfg/ReplaceTarget.java b/src/edu/purdue/cs/bloat/cfg/ReplaceTarget.java new file mode 100644 index 0000000..4d8ddbd --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/ReplaceTarget.java @@ -0,0 +1,194 @@ +/* + * Class: ReplaceTarget + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import java.util.*; + +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.JumpStmt; +import edu.purdue.cs.bloat.tree.RetStmt; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; + +/** + * ReplaceTarget replaces the block that is the target of a + * JumpStmt, JsrStmt, RetStmt, + * GotoStmt, SwitchStmt, or IfStmt with + * another Block. + */ +public class ReplaceTarget extends TreeVisitor { + Block oldDst; + + Block newDst; + + public ReplaceTarget(Block oldDst, Block newDst) { + this.oldDst = oldDst; + this.newDst = newDst; + } + + public void visitTree(Tree tree) { + Stmt last = (Stmt) tree.lastStmt(); + + if (last instanceof JumpStmt) { + JumpStmt stmt = (JumpStmt) last; + + if (FlowGraph.DEBUG) { + System.out.println(" Replacing " + oldDst + " with " + newDst + + " in " + stmt); + } + + if (stmt.catchTargets().remove(oldDst)) { + stmt.catchTargets().add(newDst); + } + + stmt.visit(this); + } + } + + public void visitJsrStmt(JsrStmt stmt) { + if (stmt.sub().entry() == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + stmt.block().graph().setSubEntry(stmt.sub(), newDst); + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + } + + public void visitRetStmt(RetStmt stmt) { + Iterator paths = stmt.sub().paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + + if (FlowGraph.DEBUG) { + System.out.println(" path = " + path[0] + " " + path[1]); + } + + if (path[1] == oldDst) { + if (FlowGraph.DEBUG) { + System.out.println(" replacing ret to " + oldDst + + " with ret to " + newDst); + } + + path[1] = newDst; + ((JsrStmt) path[0].tree().lastStmt()).setFollow(newDst); + } + } + } + + public void visitGotoStmt(GotoStmt stmt) { + if (stmt.target() == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + stmt.setTarget(newDst); + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + } + + public void visitSwitchStmt(SwitchStmt stmt) { + if (stmt.defaultTarget() == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + stmt.setDefaultTarget(newDst); + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + + Block[] targets = stmt.targets(); + + for (int i = 0; i < targets.length; i++) { + if (targets[i] == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + targets[i] = newDst; + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + } + } + + public void visitIfStmt(IfStmt stmt) { + if (stmt.trueTarget() == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + stmt.setTrueTarget(newDst); + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + + if (stmt.falseTarget() == oldDst) { + if (FlowGraph.DEBUG) { + System.out.print(" replacing " + stmt); + } + + stmt.setFalseTarget(newDst); + + if (FlowGraph.DEBUG) { + System.out.println(" with " + stmt); + } + } + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/Subroutine.java b/src/edu/purdue/cs/bloat/cfg/Subroutine.java new file mode 100644 index 0000000..1539e4f --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/Subroutine.java @@ -0,0 +1,296 @@ +/* + * Class: Subroutine + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.tree.AddressStoreStmt; +import edu.purdue.cs.bloat.tree.Tree; + +import java.io.*; +import java.util.*; + +/** + * Subroutine represents a subroutine (target of a jsr instruction) in + * java bytecode. Subroutines are used to implement the finally part of a + * try-catch-finally block. + *

+ * Each Subroutine belongs in a control flow graph, has an entry and exit block, + * and has a local variable that contains its return address. Additionally, it + * maintains a list of paths from blocks in which the subroutine is called to + * block that is executed after the subroutine returns. + *

+ * Note that it is assumed that each subroutine ends with a ret. While + * this is true for bytecode generated by javac, it is not required. + * + * @see AddressStoreStmt + * @see Block + */ + +// Important: I assume there is a ret statement for each jsr. +// This is true for javac code, but not in general. +public class Subroutine { + FlowGraph graph; // CFG containing this Subroutine + + Block entry; // Basic Block at beginning of code + + Block exit; // Basic Block ending code + + ArrayList paths; + + ReturnaddressType returnAddress; // This Subroutine's return address + + LocalVariableGen localVariable = null; + + /** + * Constructor. + * + * @param graph + * The CFG containing the block. + */ + public Subroutine(FlowGraph graph) { + this.graph = graph; + this.entry = null; + this.exit = null; + this.paths = new ArrayList(); + this.returnAddress = null; + } + + /** + * Returns the local variable containing the return address of this + * subroutine. + */ + public ReturnaddressType returnAddress() { + return returnAddress; + } + + /** + * Sets the address (stored in a LocalVariable) to which this subroutine + * will return once it is finished. + * + * @param returnAddress + * Local variable that stores the address to which the subroutine + * returns when it is completed. + * + * @see Tree#visit_astore + */ + public void setReturnAddress(ReturnaddressType returnAddress) { + this.returnAddress = returnAddress; + } + + // Set and get Local variable that is used to store the return address + // instead of the stack + public LocalVariableGen localVariable() { + return localVariable; + } + + public void setLocalVariable(LocalVariableGen localVar) { + this.localVariable = localVar; + } + + public void unsetLocalVariable() { + this.localVariable = null; + } + + /** + * Returns the number of places that this subroutine is called. + */ + public int numPaths() { + return paths.size(); + } + + /** + * Returns the paths (a Collection of two-element arrays of Blocks) that + * represent the Blocks that end in a call to this subroutine and the block + * that begin with the return address from this subroutine. + */ + public Collection paths() { + return paths; + } + + /** + * Returns the CFG that contains this subroutine. + */ + public FlowGraph graph() { + return graph; + } + + /** + * Removes all paths involving block regardless of whether it is a calling + * (source) block or a returning (target) block. + */ + public void removePathsContaining(Block block) { + for (int i = paths.size() - 1; i >= 0; i--) { + Block[] path = (Block[]) paths.get(i); + if (path[0] == block || path[1] == block) { + if (FlowGraph.DEBUG) { + System.out.println("removing path " + path[0] + " -> " + + path[1]); + } + paths.remove(i); + } + } + } + + /** + * Removes a path between a caller Block and a return Block. + */ + public void removePath(Block callerBlock, Block returnBlock) { + for (int i = 0; i < paths.size(); i++) { + Block[] path = (Block[]) paths.get(i); + if (path[0] == callerBlock && path[1] == returnBlock) { + if (FlowGraph.DEBUG) { + System.out.println("removing path " + path[0] + " -> " + + path[1]); + } + paths.remove(i); + return; + } + } + } + + /** + * Removes all caller-return paths. + */ + public void removeAllPaths() { + paths = new ArrayList(); + } + + /** + * Adds a path from the block before a Subroutine is called to a block after + * the subroutine is called. If the callerBlock is already associated with a + * returnBlock, the old returnBlock is replaced. + * + * @param callerBlock + * The block in which the subroutine is called. This Block ends + * with a jsr to this subroutine. + * @param returnBlock + * The block to which the subroutine returns. This Block begins + * at the return address of this subroutine. + */ + public void addPath(Block callerBlock, Block returnBlock) { + for (int i = 0; i < paths.size(); i++) { + Block[] path = (Block[]) paths.get(i); + if (path[0] == callerBlock) { + path[1] = returnBlock; + return; + } + } + + paths.add(new Block[] { callerBlock, returnBlock }); + } + + /** + * Returns the "return block" for a given "caller block". + */ + public Block pathTarget(Block block) { + for (int i = 0; i < paths.size(); i++) { + Block[] path = (Block[]) paths.get(i); + if (path[0] == block) { + return path[1]; + } + } + + return null; + } + + /** + * Returns the "caller block" for a given "return block". + */ + public Block pathSource(Block block) { + for (int i = 0; i < paths.size(); i++) { + Block[] path = (Block[]) paths.get(i); + if (path[1] == block) { + return path[0]; + } + } + + return null; + } + + /** + * Sets the entry Block for this Subroutine. + */ + public void setEntry(Block entry) { + this.entry = entry; + } + + /** + * Sets the exit Block for this Subroutine. + */ + public void setExit(Block exit) { + this.exit = exit; + } + + /** + * Returns the first Block in the subroutine. + */ + public Block entry() { + return entry; + } + + /** + * Returns the last Block in the subroutine. + */ + public Block exit() { + return exit; + } + + /** + * Prints a textual representation of this Subroutine. + * + * @param out + * The PrintStream to which to print. + */ + public void print(PrintStream out) { + out.println(" " + entry); + + Iterator e = paths().iterator(); + + while (e.hasNext()) { + Block[] path = (Block[]) e.next(); + out.println(" path: " + path[0] + " -> " + path[1]); + } + } + + public String toString() { + return "sub " + entry; + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/VerifyCFG.java b/src/edu/purdue/cs/bloat/cfg/VerifyCFG.java new file mode 100644 index 0000000..7958ef7 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/VerifyCFG.java @@ -0,0 +1,445 @@ +/* + * Class: VerifyCFG + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.cfg; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.tree.DefExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.RetStmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; + +import java.util.*; + +/** + * VerifyCFG visits the nodes in a control flow graph and verifies that certain + * properties of the graph are true. For instance, value numbers of expressions + * are not equal to -1, node connections are consistent, exception handlers are + * set up correctly, etc. Mostly used for debugging purposes. + */ +public class VerifyCFG extends TreeVisitor implements Optimization { + Block block; // The Block containing the node being visited + + Node parent; // The (expected) parent of the node being visited + + FlowGraph cfg; // The CFG being visited + + Set uses; // Expressions in which a definition is used + + Set nodes; // (Visited) nodes in the CFG + + boolean checkValueNumbers; // Do we check the value numbers of expressions? + + public void transform(MethodState state) { + state.controlFlowGraph().visit(this); + } + + public String traceMessage(String dateString) { + return null; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + /** + * Constructor. Don't check value numbers. + */ + public VerifyCFG() { + this(false); + } + + /** + * Constructor. Since value numbers are not strictly part of the control + * flow graph, they may or may not be checked. For instance, if a CFG is + * being verfied before value numbers are assigned, we would not want to + * check them. + * + * @param checkValueNumbers + * Are the value numbers of expressions checked? + */ + public VerifyCFG(boolean checkValueNumbers) { + this.checkValueNumbers = checkValueNumbers; + } + + /** + * Verify the specified call flow graph. Does not check the value numbers. + * + * @param cfg + * The call flow graph to verify. + */ + public static void verify(FlowGraph cfg) { + cfg.visit(new VerifyCFG()); + } + + /** + * Visit the blocks and expression trees in a control flow graph. Examine + * the uses of a variable that is defined in the CFG. Make that all uses are + * reachable (i.e. are in the CFG). + */ + public void visitFlowGraph(FlowGraph cfg) { + if (FlowGraph.DEBUG) { + System.out.println("Verifying CFG for " + cfg.method()); + } + + this.cfg = cfg; + + uses = new LinkedHashSet(); + nodes = new LinkedHashSet(); + + cfg.visitChildren(this); + + Iterator e = uses.iterator(); + + while (e.hasNext()) { + Expr use = (Expr) e.next(); + Assert.isTrue(nodes.contains(use), "use = " + use + " (" + + System.identityHashCode(use) + ") is not in the CFG"); + } + + if (FlowGraph.DEBUG) { + System.out.println("Verification successful"); + } + + this.cfg = null; + uses = null; + nodes = null; + block = null; + parent = null; + } + + /** + * First make sure that the Block indeed is in the CFG. If the + * block begins an exception handler, then make sure that all edges from + * protected blocks lead to the handler block. Also make sure that all of + * the handler block's predacessor lead to protected blocks. Finally, make + * sure that the successor/predacessor relationship holds. + */ + public void visitBlock(Block block) { + Assert.isTrue(block.graph() == cfg, block + " is not in the CFG"); + + Iterator e; + + Handler handler = (Handler) cfg.handlersMap().get(block); + + // If this block is the first block in an exception handler, make + // sure that the only predacessor edges the block has are + // protected blocks that may throw the exception handled by the + // block. Additionally, we check that there are edges from all + // protected blocks to the handler block. + // + // The predacessor to the first block in an exception handler may + // be the init block. However, it is not really a protected + // block, so just overlook it. + + if (handler != null) { + HashSet handlerPreds = new LinkedHashSet(); + + e = handler.protectedBlocks().iterator(); + + while (e.hasNext()) { + Block prot = (Block) e.next(); + handlerPreds.add(prot); + handlerPreds.addAll(cfg.preds(prot)); + } + + HashSet extra = new LinkedHashSet(cfg.preds(block)); + extra.removeAll(handlerPreds); + HashSet missing = new LinkedHashSet(handlerPreds); + missing.removeAll(cfg.preds(block)); + + Assert.isTrue((extra.size() == 0 && missing.size() == 0) + || (missing.size() == 1 && missing.contains(cfg.init())), + "Handler prots = " + handlerPreds + + " != handler block preds = " + cfg.preds(block) + + " extra = " + extra + " missing = " + missing); + } + + // Make sure that the predacessor has a successor and vice versa. + e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + Assert.isTrue(cfg.succs(pred).contains(block), pred + + " has no succ " + block); + Assert.isTrue(cfg.preds(block).contains(pred), block + + " has no pred " + pred); + } + + e = cfg.succs(block).iterator(); + + while (e.hasNext()) { + Block succ = (Block) e.next(); + Assert.isTrue(cfg.succs(block).contains(succ), block + + " has no succ " + succ); + Assert.isTrue(cfg.preds(succ).contains(block), succ + + " has no pred " + block); + } + + this.block = block; + + parent = null; + block.visitChildren(this); + } + + /** + * Make sure that all of targets of the ret are valid. The + * targets are the blocks to which the subroutine can return. + */ + public void visitRetStmt(RetStmt stmt) { + Set targets = new LinkedHashSet(); + + Iterator iter = stmt.sub().paths().iterator(); + + while (iter.hasNext()) { + Block[] path = (Block[]) iter.next(); + targets.add(path[1]); + } + + targets.addAll(stmt.catchTargets()); + + verifyTargets(stmt.block(), targets); + + visitNode(stmt); + } + + /** + * Make sure that all of the targets of the jsr are valid. The + * only target is the entry block of the subroutine. + */ + public void visitJsrStmt(JsrStmt stmt) { + Set targets = new LinkedHashSet(); + targets.add(stmt.sub().entry()); + targets.addAll(stmt.catchTargets()); + verifyTargets(stmt.block(), targets); + + visitNode(stmt); + } + + /** + * Make sure that that all of the targets of the switch are valid. + */ + public void visitSwitchStmt(SwitchStmt stmt) { + Set targets = new LinkedHashSet(); + + targets.add(stmt.defaultTarget()); + + for (int i = 0; i < stmt.targets().length; i++) { + targets.add(stmt.targets()[i]); + } + + targets.addAll(stmt.catchTargets()); + + verifyTargets(stmt.block(), targets); + + visitNode(stmt); + } + + /** + * Make sure that the targets of the if statement are valid. Targets consist + * of the true target, the false target, and the first blocks of any + * exceptions that may be thrown by the if statement. + */ + public void visitIfStmt(IfStmt stmt) { + Set targets = new LinkedHashSet(); + targets.add(stmt.trueTarget()); + targets.add(stmt.falseTarget()); + targets.addAll(stmt.catchTargets()); + verifyTargets(stmt.block(), targets); + + visitNode(stmt); + } + + /** + * Make sure that the target of goto is valid. + */ + public void visitGotoStmt(GotoStmt stmt) { + Set targets = new LinkedHashSet(); + targets.add(stmt.target()); + targets.addAll(stmt.catchTargets()); + verifyTargets(stmt.block(), targets); + + visitNode(stmt); + } + + /** + * Verifies information about the targets of a jump in a given block. First, + * make sure that the number of targets is the same as the number of + * sucessor nodes to the block. Make sure that targets of all of the jumps + * are indeed in the CFG. Make sure that every target is a successor of the + * block. + */ + private void verifyTargets(Block block, Set targets) { + Assert.isTrue(targets.size() == cfg.succs(block).size(), block + + " has succs " + cfg.succs(block) + " != " + targets + " in " + + block.tree().lastStmt()); + + Iterator iter = targets.iterator(); + + while (iter.hasNext()) { + Block target = (Block) iter.next(); + Assert.isTrue(block.graph().hasNode(target), target + + " is not in the CFG"); + Assert.isTrue(cfg.succs(block).contains(target), target + + " is not a succ of " + block + " " + + block.tree().lastStmt()); + } + } + + /** + * If desired, makes sure that the store expression's value number is not + * -1. Makes sure that the store expression's block and parent Node + * are what we expect them to be. If the type of the StoreExpr is + * void, then make sure that its parent is an ExprStmt (i.e. make + * sure it is not nested within another expression). + */ + public void visitStoreExpr(StoreExpr node) { + nodes.add(node); + + if (checkValueNumbers) { + Assert.isTrue(node.valueNumber() != -1, node + + ".valueNumber() = -1"); + } + + Assert.isTrue(node.block() == block, node + ".block() = " + + node.block() + " != block = " + block); + Assert.isTrue(node.parent() == parent, node + ".parent() = " + + node.parent() + " != parent = " + parent); + + // Visit the MemExpr into which node stores. + parent = node; + node.target().visit(this); + + // Visit the expression whose value is being stored by node + parent = node; + node.expr().visit(this); + + parent = node.parent(); + + if (node.type().equals(Type.VOID)) { + Assert.isTrue(parent instanceof ExprStmt, "parent of " + node + + " = " + parent + " is not an ExprStmt"); + } + } + + /** + * Make sure that the Node resides in the block that we expect it + * to and that it has the expected parent expression tree Node. + * Make sure that the children of this Node are also correct. + */ + public void visitNode(Node node) { + nodes.add(node); + + Assert.isTrue(node.block() == block, node + ".block() = " + + node.block() + " != block = " + block); + Assert.isTrue(node.parent() == parent, node + ".parent() = " + + node.parent() + " != parent = " + parent); + + final ArrayList children = new ArrayList(); + + node.visitChildren(new TreeVisitor() { + public void visitNode(Node n) { + children.add(n); + } + }); + + Iterator e = children.iterator(); + + while (e.hasNext()) { + Node child = (Node) e.next(); + parent = node; + child.visit(this); + } + + parent = node.parent(); + } + + /** + * If desired, make sure that the value number of the Expr is not + * -1. + */ + public void visitExpr(Expr expr) { + if (checkValueNumbers) { + Assert.isTrue(expr.valueNumber() != -1, expr + + ".valueNumber() = -1"); + } + + visitNode(expr); + } + + /** + * Keep track of all the uses of the expression defined by the + * DefExpr. This information is used when verifying the + * FlowGraph. + */ + public void visitDefExpr(DefExpr expr) { + uses.addAll(expr.uses()); + visitExpr(expr); + } + + /** + * Make sure that the VarExpr either defines a local variable, is + * defined by another expression, or is the child of a PhiStmt + * (therefore making the VarExpr a phi-variable). + */ + public void visitVarExpr(VarExpr expr) { + Assert.isTrue(expr.isDef() || expr.def() != null + || expr.parent() instanceof PhiStmt, "Null def for variable " + + expr); + + visitDefExpr(expr); + } +} diff --git a/src/edu/purdue/cs/bloat/cfg/package.html b/src/edu/purdue/cs/bloat/cfg/package.html new file mode 100644 index 0000000..30e8aa9 --- /dev/null +++ b/src/edu/purdue/cs/bloat/cfg/package.html @@ -0,0 +1,14 @@ + + + +

Classes to represent a Java method as a control flow graph of basic +blocks. A basic block is a sequence of code that is entered +only in once place (e.g. the target of a branch statement) and is +exited at only one place (e.g. a branch statement). Each basic block +consists of an expression tree. There are also classes to represent +try-catch blocks, Java subroutines (finally blocks), certain +properties of the control flow graph such as its dominator tree, and +to visualize a control flow graph.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/class.mk b/src/edu/purdue/cs/bloat/class.mk new file mode 100644 index 0000000..5e976ab --- /dev/null +++ b/src/edu/purdue/cs/bloat/class.mk @@ -0,0 +1,47 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +.SUFFIXES: .java .class +JAVA_HOME = /usr/local +CP := $(CLASSPATH) +#JAVA_HOME = /gcm/where/jdk/1.3/sparc.Solaris +JAVAC = $(JAVA_HOME)/bin/javac +JFLAGS = -g +CLASSPATH = $(JAVA_HOME)/lib/classes.zip:$(CP) + +all: class + +clean: + rm -f *.class + +class: + @files=`$(MAKE) -n _class | grep $(JAVAC) | cut -d' ' -f4`; \ + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + if [ "x$$files" != "x" ]; then \ + echo $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \ + $(JAVAC) $(JFLAGS) -classpath $$cpath $$files; \ + fi + +_class: $(CLASS) + +.java.class: + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAC) -classpath $$cpath $< diff --git a/src/edu/purdue/cs/bloat/codegen/CodeGenerator.java b/src/edu/purdue/cs/bloat/codegen/CodeGenerator.java new file mode 100644 index 0000000..1f79706 --- /dev/null +++ b/src/edu/purdue/cs/bloat/codegen/CodeGenerator.java @@ -0,0 +1,2735 @@ +/* + * Class: CodeGeneration + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.codegen; + +import org.apache.bcel.generic.*; +import org.apache.bcel.Constants; +import org.apache.bcel.generic.CodeExceptionGen; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Handler; +import edu.purdue.cs.bloat.cfg.ReplaceTarget; +import edu.purdue.cs.bloat.cfg.Subroutine; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.tree.AddressStoreStmt; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallMethodExpr; +import edu.purdue.cs.bloat.tree.CallStaticExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.CheckExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.DefExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfCmpStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.IfZeroStmt; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.InstanceOfExpr; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.JumpStmt; +import edu.purdue.cs.bloat.tree.LabelStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MemExpr; +import edu.purdue.cs.bloat.tree.MonitorStmt; +import edu.purdue.cs.bloat.tree.NegExpr; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.ReplaceVisitor; +import edu.purdue.cs.bloat.tree.RetStmt; +import edu.purdue.cs.bloat.tree.ReturnAddressExpr; +import edu.purdue.cs.bloat.tree.ReturnExprStmt; +import edu.purdue.cs.bloat.tree.ReturnStmt; +import edu.purdue.cs.bloat.tree.ShiftExpr; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.StackOptimizationData; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.ThrowStmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.UCExpr; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.tree.ZeroCheckExpr; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.ImmutableIterator; + +import java.util.*; + +/** + * CodeGenerator performs some final optimizations and is used (via a visitor) + * to generate bytecode for the contents of a control flow graph. + * + * @see FlowGraph + */ +public class CodeGenerator extends TreeVisitor { + public static boolean DEBUG = false; + + public static boolean USE_PERSISTENT = false; // Generate _nowb + + /** + * Use information about placement of local variables to eliminate loads and + * stores in favor of stack manipulations + */ + public static boolean OPT_STACK = false; + + public static boolean DB_OPT_STACK = false; + + // Statics from UseMapStore. + public static boolean PRESERVE_DEBUG = false; + + public static boolean UNIQUE_HANDLERS = false; + + public static boolean OPT_STACK_2 = false; // byte-code level stack opt + + protected MethodGen method; + + InstructionFactory instFactory = null; // FACTORY + + protected MethodState state = null; + + protected Set visited; + + protected Map postponedInstructions; + + protected Block next; + + private InstructionHandle lastAdded = null; // remembers the location of the + + // last added insruction + + private Map handleMap = new LinkedHashMap(); + + protected int stackHeight; // The current height of the stack + + StackOptimizationData currentSO; // object used to determine where to + + // apply + // stack optimization + + /** + * Constructor that takes the whole MethodState. + * + * This allows the code generator to be invoked in some very nice ways. + * + * @param state + * The method state for which bytecode is to be generated. + */ + public CodeGenerator(MethodState state) { + this(state.methodGen()); + this.state = state; + } + + /** + * Constructor. + * + * @param method + * The method for which bytecode is generated. + */ + public CodeGenerator(MethodGen method) { + this.method = method; + // method.setInstructionList(new InstructionList()); + // method.removeExceptionHandlers(); + // method.removeLocalVariables(); + // method.removeLineNumbers(); + + this.postponedInstructions = new LinkedHashMap(); + + instFactory = new InstructionFactory(method.getConstantPool()); // FACTORY + } + + public CodeGenerator generateCode() { + method.setInstructionList(new InstructionList()); + method.removeExceptionHandlers(); + method.removeLocalVariables(); + method.removeLineNumbers(); + generateCode(state.controlFlowGraph()); + return this; + } + + public void generateCode(FlowGraph cfg) { + cfg.visit(this); + } + + // If Codegen worked by writing out the FlowGraph to bytecode in trace + // order we would be left + // with branch targets into a no longer correct InstructionList. These + // could be translated to + // the new InstructionHandles using a map of old to new Label + // statements. + // To avoid branch targets into a no longer existing InstructionList we + // commit all Label + // instructions first (in trace order) we also maintain a map of old to + // new Label instructions. + // In this way when we encounter a Label statement we moved + // lastAdded(the insertion point for + // committed instructions) to the appropriate InstructionHandle and can + // avoid possible branch + // targets into a invalid InstructionList --Arrin + + // This method allows us the fetch the new Label(InstructionHandle) from + // the map. + private InstructionHandle getLabelInstructionHandle(InstructionHandle ih) { + InstructionHandle target = (InstructionHandle) handleMap.get(ih); + Assert.isTrue(target != null, + "Unable to find a corresponding InstructionHandle for block" + + ih); + return target; + } + + /** + * Visits the nodes in the method's control flow graph and ensures that + * information about the method's basic blocks is consistent and correct. + * + * @param cfg + * The control flow graph associated with this method. + */ + public void visitFlowGraph(final FlowGraph cfg) { + // Generate the code. + + visited = new LinkedHashSet(); + visited.add(cfg.source()); + visited.add(cfg.sink()); + + Iterator e = cfg.trace().iterator(); + + Assert.isTrue(e.hasNext(), "trace is empty"); + + stackHeight = 0; // At beginning of method stack has height 0 + Block block = null; + while (e.hasNext()) { + block = (Block) e.next(); + // Make sure the first label is marked as starting a block + // and the rest are marked as not starting a block. + block.visit(new TreeVisitor() { + public void visitLabelStmt(LabelStmt stmt) { + FlowGraph.setLabel(stmt.label()); + InstructionHandle ih; + ih = method.getInstructionList().append(new NOP()); + // Unfortunately as we don't yet know what instruction + // to add it has to be a NOP + FlowGraph.setLabel(ih); + handleMap.put(stmt.label(), ih); + // map the old Label to the new one + // add NOP here and add the InstructionHandle to the Map + } + + public void visitStmt(Stmt stmt) { + } + }); + } + + e = cfg.trace().iterator(); + + block = (Block) e.next(); + + // Visit each block in the method (via the trace in the method's CFG) + // and ensure that the first (and ONLY the first) label in the code + // is marked as starting a block. + while (block != null) { + if (e.hasNext()) { + next = (Block) e.next(); + + } else { + next = null; + } + + if (CodeGenerator.DEBUG) { + System.out.println("code for " + block); + } + + // Generate the code for each block + visited.add(block); + // currentSO is the StackOptimizationData object that discerns + // where dups may be used instead of loads + if (OPT_STACK) + currentSO = block.stackOptimizer(); + // lastAdded = method.getInstructionList().getStart(); + block.visitChildren(this); + + block = next; + } + + Assert.isTrue(visited.size() == cfg.size(), + "did not visit all blocks while generating code"); + + next = null; + visited = null; + + // Go through the catch blocks and determine the what the + // protected regions are that correspond to the catch blocks. + // Create Handler objects to represent the protected regions. + + Iterator iter = cfg.catchBlocks().iterator(); + + while (iter.hasNext()) { + + Block catchBlock = (Block) iter.next(); + Handler handler = (Handler) cfg.handlersMap().get(catchBlock); + + Type type = handler.catchType(); + + if (type.equals(Type.NULL)) { + type = null; + } + + // First block in protected block + Block begin = null; + Block last = null; + Iterator blocks = cfg.trace().iterator(); + + while (blocks.hasNext()) { + block = (Block) blocks.next(); + + if (handler.protectedBlocks().contains(block)) { + if (begin == null) { + begin = block; + } + last = block; + + } else if (begin != null) { + // The block is no longer protected, its the end of the + // protected region + // Assigned the last handle of the previous block + InstructionHandle temp = getLabelInstructionHandle(last + .label()); + if (temp.getNext() != null + && !FlowGraph.label(temp.getNext())) { + temp = temp.getNext(); + } + InstructionHandle beginTarget = getLabelInstructionHandle(begin + .label()); + InstructionHandle catchTarget = getLabelInstructionHandle(catchBlock + .label()); + method.addExceptionHandler(beginTarget,// temp, + ((InstructionHandle) handleMap.get(catchBlock + .label())).getPrev(), catchTarget, + (ObjectType) type); + begin = null; + last = null; + } + } + } + + FlowGraph.removeNOPs(method); + method.getInstructionList().setPositions(); + method.setMaxStack(); + method.setMaxLocals(); + method.update(); + + if (DEBUG) { + System.out.println("Method:"); + System.out.println(method); + for (InstructionHandle ih = method.getInstructionList().getStart(); ih != null; ih = ih + .getNext()) { + if (FlowGraph.label(ih)) + System.out.print("(L)"); + else + System.out.print(" "); + System.out.print(" " + ih.getPosition() + ": " + + ih.getInstruction()); + if (ih.getAttribute("Def") != null) + System.out.print("(" + ih.getAttribute("Def") + ")"); + System.out.println(""); + } + // System.out.println(method.getMethod().getCode()); + System.out.println(); + System.out.println("LocalVariables: "); + LocalVariableGen[] locals = method.getLocalVariables(); + for (int i = 0; i < locals.length; i++) { + System.out.println(" " + locals[i]); + } + System.out.println("LocalVariable size: " + method.getMaxLocals()); + System.out.println("Stack size: " + method.getMaxStack()); + CodeExceptionGen[] excptns = method.getExceptionHandlers(); + if (excptns != null) { + System.out.println("ExceptionHandlers: "); + for (int j = 0; j < excptns.length; j++) + System.out.println(" " + excptns[j]); + } + } + + } + + public CodeGenerator simplifyControlFlow() { + simplifyControlFlow(state.controlFlowGraph()); + return this; + } + + /** + * Simplifies the control flow of a method by changing jump and return + * statements into gotos where appropriate. + */ + public void simplifyControlFlow(final FlowGraph cfg) { + // Remove any blocks from the CFG that consist of solely jumps + removeEmptyBlocks(cfg); + + cfg.visit(new TreeVisitor() { + public void visitJsrStmt(JsrStmt stmt) { + final Subroutine sub = stmt.sub(); + + // If there is only 1 path through the sub, replace both + // the jsr and the ret with gotos. + if (sub.numPaths() == 1) { + Block exit = sub.exit(); + + // Remember that it is not required for a subroutine to + // have + // a ret. So, no exit block may be identified and we'll + // have to make sure one exists. + if (exit != null) { + JumpStmt oldJump = (JumpStmt) exit.tree().lastStmt(); + JumpStmt jump = new GotoStmt(stmt.follow()); + jump.catchTargets().addAll(oldJump.catchTargets()); + oldJump.replaceWith(jump); + } + + JumpStmt jump = new GotoStmt(sub.entry()); + jump.catchTargets().addAll(stmt.catchTargets()); + stmt.replaceWith(jump); + + // The subroutine is no longer really a subroutine + cfg.removeSub(sub); + + // Clean up the CFG by removing all AddressStoreStmts + // that + // store the address of the "removed" subroutine. + cfg.visit(new TreeVisitor() { + Iterator iter; + + public void visitTree(Tree tree) { + iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + + if (s instanceof AddressStoreStmt) { + AddressStoreStmt store = (AddressStoreStmt) s; + + if (store.sub() == sub) { + iter.remove(); + } + } + } + } + }); + } + } + + public void visitStmt(Stmt stmt) { + } + }); + } + + public CodeGenerator replacePhis() { + replacePhis(state.controlFlowGraph()); + return this; + } + + /** + * Replace PhiStmts with copies that accomplish what the PhiStmts represent. + * Then remove the PhiStmts from the control flow graph. + */ + public void replacePhis(final FlowGraph cfg) { + replaceCatchPhis(cfg); + replaceJoinPhis(cfg); + + // Remove the phis. + cfg.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + Iterator e = tree.stmts().iterator(); + + while (e.hasNext()) { + Stmt s = (Stmt) e.next(); + + if (s instanceof PhiStmt) { + e.remove(); + } + } + } + }); + } + + /** + * Replace each PhiCatchStmt with assignments at its operands' defs. + */ + private void replaceCatchPhis(final FlowGraph cfg) { + cfg.visit(new TreeVisitor() { + LinkedHashMap seen = new LinkedHashMap(); + + public void visitFlowGraph(FlowGraph graph) { + Iterator iter = graph.catchBlocks().iterator(); + + // Examine each block that begins an exception handler + while (iter.hasNext()) { + Block block = (Block) iter.next(); + block.visit(this); + } + } + + public void visitPhiCatchStmt(PhiCatchStmt phi) { + LocalExpr target = (LocalExpr) phi.target(); + int index = target.index(); + + Iterator iter = phi.operands().iterator(); + + // Examine every operand of the PhiCatchStmt. If necessary, + // insert copies of the operand to the target after the last + // occurrence of the operand. + while (iter.hasNext()) { + LocalExpr expr = (LocalExpr) iter.next(); + LocalExpr def = (LocalExpr) expr.def(); + + if (def == null) { + continue; + } + + if (DEBUG) { + System.out.println("inserting for " + phi + " at " + + def); + } + + BitSet s = (BitSet) seen.get(def); + + if (s == null) { + s = new BitSet(); + seen.put(def, s); + + final BitSet t = s; + + // Visit the parent expression and make note of which + // local variables were encountered in StoreExprs. That + // is, have we already generated a copy for the operand + // of + // interest? + def.parent().visit(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + if (DEBUG) { + System.out.println(" merging with " + + expr); + } + + Expr lhs = expr.target(); + Expr rhs = expr.expr(); + + if (lhs instanceof LocalExpr) { + t.set(((LocalExpr) lhs).index()); + } + + if (rhs instanceof LocalExpr) { + t.set(((LocalExpr) rhs).index()); + + } else if (rhs instanceof StoreExpr) { + // Visit RHS. LHS be ignored by + // visitNode. + super.visitStoreExpr(expr); + } + } + + public void visitNode(Node node) { + } + }); + } + + // If we've already inserted a copy (StoreStmt) for the + // local variable, skip it + if (s.get(index)) { + continue; + } + + s.set(index); + + Assert.isTrue(def != null); + + if (def.parent() instanceof Stmt) { + // Insert a new Stmt to copy into the target + + Stmt stmt = (Stmt) def.parent(); + Stmt store = createStore(target, def); + def.block().tree().addStmtAfter(store, stmt); + + } else { + Assert.isTrue(def.parent() instanceof StoreExpr); + + // Replace s := r with s := (t := r) + StoreExpr p = (StoreExpr) def.parent(); + Expr rhs = p.expr(); + + if (rhs instanceof LocalExpr + && ((LocalExpr) rhs).index() == def.index()) { + // No need to insert a copy. Just change the + // index + // (local variable to which LocalExpr is + // assigned) to be + // the same as the target + def.setIndex(index); + + } else { + rhs.setParent(null); + + // Copy the rhs into the target + StoreExpr store = new StoreExpr((LocalExpr) target + .clone(), rhs, rhs.type()); + + p.visit(new ReplaceVisitor(rhs, store)); + } + } + } + } + + public void visitStmt(Stmt stmt) { + } + }); + } + + /** + * Replace PhiJoinStmts with assignments at the end of the predecessor + * blocks. Note that from now on the FUD chains are broken since there can + * be more than one def of a variable. + */ + private void replaceJoinPhis(final FlowGraph cfg) { + // Go in trace order since liveness was computed under this + // assumption. + + Iterator iter = cfg.trace().iterator(); + + while (iter.hasNext()) { + Block block = (Block) iter.next(); + + if (block == cfg.sink()) { + continue; + } + + block.visit(new TreeVisitor() { + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + // If an operand of the Phi statement is undefined, + // insert + // code to assign 0 to the operand. The value should + // never + // be used, but the verifier will squawk about using an + // undefined local variable. + + Iterator preds = cfg.preds(stmt.block()).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + + Expr operand = stmt.operandAt(pred); + + if (stmt.target() instanceof LocalExpr + && operand instanceof LocalExpr) { + + LocalExpr t = (LocalExpr) stmt.target(); + LocalExpr s = (LocalExpr) operand; + + if (t.index() == s.index()) { + // The target and the operand are already + // allocated to + // the same variable. Don't bother making a + // copy. + continue; + } + } + + final Tree tree = pred.tree(); + + // Insert stores before the last stmt to ensure + // we don't redefine locals used the the branch stmt. + final Stmt last = tree.lastStmt(); + + last.visitChildren(new TreeVisitor() { + // The last statement in the block should be a + // jump. If + // the jump statement contains an expression, + // replace + // that expression with a stack variable. Before + // the + // jump, insert a store of the expression into + // the stack + // variable. This is done so that the store to + // the + // PhiJoinStmt's operand does not interfere with + // any + // local variables that might appear in the + // expression. + // + // operand = ... + // JUMP (exp) + // | + // v + // target = PhiJoin(operand) + // ... + // Becomes + // + // operand = ... + // var = exp + // target = operand + // JUMP (var) + // | + // v + // target = PhiJoin(operand) // Removed later + // ... + + public void visitExpr(Expr expr) { + StackExpr var = tree.newStack(expr.type()); + var.setValueNumber(expr.valueNumber()); + + Node p = expr.parent(); + expr.setParent(null); + p.visit(new ReplaceVisitor(expr, var)); + + var = (StackExpr) var.clone(); + StoreExpr store = new StoreExpr(var, expr, expr + .type()); + store.setValueNumber(expr.valueNumber()); + + Stmt storeStmt = new ExprStmt(store); + storeStmt.setValueNumber(expr.valueNumber()); + + tree.addStmtBeforeJump(storeStmt); + } + + public void visitStackExpr(StackExpr expr) { + } + }); + + Stmt store = createStore(stmt.target(), operand); + + if (CodeGenerator.DEBUG) { + System.out.println("CodeGen: insert for " + stmt + + " " + store + " in " + pred); + } + + tree.addStmtBeforeJump(store); + } + } + + public void visitStmt(Stmt stmt) { + } + }); + } + } + + /** + * Removes blocks that contain no other statements than gotos, jumps, + * returns, or labels. Other blocks that are invovled with the blocks being + * removed are updated appropriately. + */ + private void removeEmptyBlocks(final FlowGraph cfg) { + + Set emptyBlocks = new LinkedHashSet(); + + Iterator e = cfg.nodes().iterator(); + + BLOCKS: while (e.hasNext()) { + Block block = (Block) e.next(); + + // Collect any blocks that contain only gotos, + // jsrs, rets, or labels. + Iterator stmts = block.tree().stmts().iterator(); + + while (stmts.hasNext()) { + Stmt stmt = (Stmt) stmts.next(); + + if (stmt instanceof GotoStmt || stmt instanceof JsrStmt + || stmt instanceof RetStmt || stmt instanceof LabelStmt) { + continue; + } + + // The block contains something other than the above, it is + // not empty. + continue BLOCKS; + } + + emptyBlocks.add(block); + } + + // We want to keep the source, init, and sink blocks even if + // they're empty + emptyBlocks.remove(cfg.source()); + emptyBlocks.remove(cfg.init()); + emptyBlocks.remove(cfg.sink()); + + // Did the CFG change? + boolean changed = true; + + while (changed) { + changed = false; + + // Exclude the blocks that are control dependent on other + // blocks. + Set empty = new LinkedHashSet(emptyBlocks); + empty.removeAll(cfg.iteratedPdomFrontier(cfg.nodes())); + + e = empty.iterator(); + + EMPTY: while (e.hasNext()) { + Block block = (Block) e.next(); + + Stmt last = block.tree().lastStmt(); + + Assert.isTrue(last instanceof GotoStmt + || last instanceof JsrStmt || last instanceof RetStmt); + + if (last instanceof GotoStmt) { + // All a block does is jump to another block + // + // jmp ... L + // L: goto M + // => + // jmp ... M + Block target = ((GotoStmt) last).target(); + + Iterator preds = new ImmutableIterator(cfg.preds(block)); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + Stmt predLast = pred.tree().lastStmt(); + predLast.visit(new ReplaceTarget(block, target)); + + cfg.removeEdge(pred, block); + cfg.addEdge(pred, target); + + changed = true; + } + + } else if (last instanceof RetStmt) { + // All a subroutine does is return + + Iterator preds = new ImmutableIterator(cfg.preds(block)); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + Stmt predLast = pred.tree().lastStmt(); + + if (predLast instanceof JsrStmt) { + // The previous block is the jsr... + // + // jsr L ret to M + // M: ... + // L: ret // The body of the subroutine is empty + // => + // goto M + // M: ... + + JsrStmt stmt = (JsrStmt) predLast; + + JumpStmt jump = new GotoStmt(stmt.follow()); + jump.catchTargets().addAll(stmt.catchTargets()); + stmt.replaceWith(jump); + + stmt.sub().removePathsContaining(pred); + + } else if (predLast instanceof GotoStmt) { + // The previous block ends in a goto. Move the + // ret up + // into the previous block, update catch targets + // of any + // exceptions thrown by the block terminated by + // the + // jump, and update the subroutine's exit block + // to be + // the previous block (in which the ret now + // resides). + + JumpStmt jump = (RetStmt) last.clone(); + jump.catchTargets().addAll( + ((JumpStmt) predLast).catchTargets()); + predLast.replaceWith(jump); + ((RetStmt) last).sub().setExit(pred); + } + + // Remove the block from the CFG + cfg.succs(pred).remove(block); + cfg.succs(pred).addAll(cfg.succs(block)); + + changed = true; + } + + } else if (last instanceof JsrStmt) { + // All the block does is a jsr + // + // goto L + // L: jsr M + // => + // jsr M + // L: jsr M + Iterator preds = new ImmutableIterator(cfg.preds(block)); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + Stmt predLast = pred.tree().lastStmt(); + + if (predLast instanceof GotoStmt) { + JsrStmt stmt = (JsrStmt) last; + + JumpStmt jump = new JsrStmt(stmt.sub(), stmt + .follow()); + jump.catchTargets().addAll( + ((JumpStmt) predLast).catchTargets()); + predLast.replaceWith(jump); + + // The block is no longer a viable caller of the + // subroutine + stmt.sub().removePathsContaining(block); + stmt.sub().addPath(pred, stmt.follow()); + + cfg.addEdge(pred, stmt.sub().entry()); + cfg.removeEdge(pred, block); + + changed = true; + } + } + } else if (last == null) {// MOD There is only a LabelSmt + // in block + Iterator preds = new ImmutableIterator(cfg.preds(block)); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + cfg.succs(pred).remove(block); + } + changed = true; + } else { + throw new RuntimeException(); + } + } + + if (changed) { + cfg.removeUnreachable(); + + // Remove any empty blocks that we've already deleted. + emptyBlocks.retainAll(cfg.nodes()); + } + } + } + + public CodeGenerator allocateReturnAddresses(RegisterAllocator alloc) { + Assert.isNotNull(alloc); + allocReturnAddresses(state.controlFlowGraph(), alloc); + return this; + } + + /** + * Allocate "registers" (LocalVariables) for the return addresses for each + * subroutine in the method. + * + * @param cfg + * Control flow graph for the method + * @param alloc + * Allocation (and information about) the local variables in the + * method. + * + * @see LocalVariable + * @see LocalExpr + */ + public void allocReturnAddresses(FlowGraph cfg, + final RegisterAllocator alloc) { + // Allocate registers for the returnAddresses. Don't bother trying + // to minimize the number of locals, just get a new one. + Iterator e = cfg.subroutines().iterator(); + + while (e.hasNext()) { + Subroutine sub = (Subroutine) e.next(); + LocalVariableGen var = alloc.newLocal(sub.returnAddress()); + sub.setLocalVariable(var); + } + } + + /** + * Create a ExprStmt that initializes a target variable to a default value + * based on the type of the target. + */ + protected Stmt createUndefinedStore(VarExpr target) { + if (target.type() instanceof ReferenceType) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr(null, + Type.OBJECT), target.type())); + } + + if (Assert.isIntegral(target.type())) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr( + new Integer(0), Type.INT), target.type())); + } + + if (target.type().equals(Type.LONG)) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr( + new Long(0), Type.LONG), target.type())); + } + + if (target.type().equals(Type.FLOAT)) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr( + new Float(0.0F), Type.FLOAT), target.type())); + } + + if (target.type().equals(Type.DOUBLE)) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr( + new Double(0.0), Type.DOUBLE), target.type())); + } + throw new RuntimeException("Illegal type: " + target.type()); + } + + /** + * Returns an ExprStmt that contains a store of the source into the target. + */ + protected Stmt createStore(VarExpr target, Expr source) { + target = (VarExpr) target.clone(); + // WHY clone them? + + // Source is an undefined variable, initialize it + // if (source instanceof VarExpr && source.def() == null){ + // return createUndefinedStore(target); + // } + return new ExprStmt(new StoreExpr(target, (Expr) source.clone(), // Why + // clone + // them? + target.type())); + } + + /* + * Using an InstructionVisitor generate the code... + */ + + // Several of the visit methods contain code for stack + // optimization (place dups and swaps and eliminate temporary + // variables). Nodes where swaps are to be placed are so + // marked. The markings may appear at IfCmpStmt, InitStmt, + // StoreExpr, ArithExpr, ArrayRefExpr, CallMethodExpr, + // CallStaticExpr, NewMultiArrayExpr, ShiftExpr. + public void visitExpr(Expr expr) { + throw new RuntimeException("Unhandled expression type: " + + expr.getClass().getName()); + } + + public void visitExprStmt(ExprStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + if (!(stmt.expr() instanceof StoreExpr)) { + if (!stmt.expr().type().equals(Type.VOID)) { + if (stmt.expr().type().getSize() == 2) { + lastAdded = method.getInstructionList().append(lastAdded, + new POP2()); + stackHeight -= 2; + + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new POP()); + stackHeight -= 1; + } + } + } + } + + public void visitInitStmt(InitStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + } + + public void visitGotoStmt(GotoStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + // genPostponed(stmt); + + Block targetBlock = stmt.target(); + if (targetBlock != next) { + InstructionHandle target = getLabelInstructionHandle(targetBlock + .label()); + lastAdded = method.getInstructionList().append(lastAdded, + new GOTO(target)); + } + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + Block t = stmt.trueTarget(); + Block f = stmt.falseTarget(); + + if (f == next) { + // Fall through to the false branch. + genIfCmpStmt(stmt); + + } else if (t == next) { + // Fall through to the true branch. + stmt.negate(); + genIfCmpStmt(stmt); + + } else { + // Generate a goto to the false branch after the if statement. + genIfCmpStmt(stmt); + + // method.addLabel(method.newLabelTrue()); //Tom changed to say + // "True" + InstructionHandle falseTarget = getLabelInstructionHandle(f.label()); + lastAdded = method.getInstructionList().append(lastAdded, + new GOTO(falseTarget)); + FlowGraph.setLabel(lastAdded);// Just in case it's needed + } + } + + private void genIfCmpStmt(IfCmpStmt stmt) { + BranchInstruction newInst; + stmt.visitChildren(this); + + // genPostponed(stmt); + + int cmp = stmt.comparison(); + // Find the appropriate LabelStmt in the new list. + InstructionHandle trueTarget = getLabelInstructionHandle(stmt + .trueTarget().label()); + + if (stmt.left().type() instanceof ReferenceType) { + Assert.isTrue((stmt.right().type() instanceof ReferenceType), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + newInst = new IF_ACMPEQ(trueTarget); + break; + case IfStmt.NE: + newInst = new IF_ACMPNE(trueTarget); + break; + default: + throw new RuntimeException(); + } + + } else { + Assert.isTrue(Assert.isIntegral(stmt.left().type()), + "Illegal statement: " + stmt); + Assert.isTrue(Assert.isIntegral(stmt.right().type()), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + newInst = new IF_ICMPEQ(trueTarget); + break; + case IfStmt.NE: + newInst = new IF_ICMPNE(trueTarget); + break; + case IfStmt.GT: + newInst = new IF_ICMPGT(trueTarget); + break; + case IfStmt.GE: + newInst = new IF_ICMPGE(trueTarget); + break; + case IfStmt.LT: + newInst = new IF_ICMPLT(trueTarget); + break; + case IfStmt.LE: + newInst = new IF_ICMPLE(trueTarget); + break; + default: + throw new RuntimeException(); + } + } + + lastAdded = method.getInstructionList().append(lastAdded, newInst); + stackHeight -= 2; + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + Block t = stmt.trueTarget(); + Block f = stmt.falseTarget(); + + if (f == next) { + // Fall through to the false branch. + genIfZeroStmt(stmt); + } else if (t == next) { + // Fall through to the true branch. + stmt.negate(); + genIfZeroStmt(stmt); + } else { + // Generate a goto to the false branch after the if statement. + genIfZeroStmt(stmt); + InstructionHandle falseTarget = getLabelInstructionHandle(f.label()); + + lastAdded = method.getInstructionList().append(lastAdded, + new GOTO(falseTarget)); + FlowGraph.setLabel(lastAdded);// Just in case it's needed + } + } + + private void genIfZeroStmt(IfZeroStmt stmt) { + BranchInstruction inst; + InstructionHandle trueTarget = getLabelInstructionHandle(stmt + .trueTarget().label()); + + stmt.expr().visit(this); + + // genPostponed(stmt); + + int cmp = stmt.comparison(); + + if (stmt.expr().type() instanceof ReferenceType) { + switch (cmp) { + case IfStmt.EQ: + inst = new IFNULL(trueTarget); + break; + case IfStmt.NE: + inst = new IFNONNULL(trueTarget); + break; + default: + throw new RuntimeException(); + } + + } else { + Assert.isTrue(Assert.isIntegral(stmt.expr().type()), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + inst = new IFEQ(trueTarget); + break; + case IfStmt.NE: + inst = new IFNE(trueTarget); + break; + case IfStmt.GT: + inst = new IFGT(trueTarget); + break; + case IfStmt.GE: + inst = new IFGE(trueTarget); + break; + case IfStmt.LT: + inst = new IFLT(trueTarget); + break; + case IfStmt.LE: + inst = new IFLE(trueTarget); + break; + default: + throw new RuntimeException(); + } + } + lastAdded = method.getInstructionList().append(lastAdded, inst); + stackHeight -= 1; + } + + public void visitLabelStmt(LabelStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + lastAdded = getLabelInstructionHandle(stmt.label()); + // change the lastAdded InstructionHandle so that instructions are now + // added + // after this label inside the block this label starts. --Arrin + } + + public void visitMonitorStmt(MonitorStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + if (stmt.kind() == MonitorStmt.ENTER) { + lastAdded = method.getInstructionList().append(lastAdded, + new MONITORENTER()); + stackHeight -= 1; + + } else if (stmt.kind() == MonitorStmt.EXIT) { + lastAdded = method.getInstructionList().append(lastAdded, + new MONITOREXIT()); + stackHeight -= 1; + + } else { + throw new IllegalArgumentException(); + } + } + + public void visitPhiStmt(PhiStmt stmt) { + throw new RuntimeException("Cannot generate code for " + stmt); + } + + // I haven't done anything with the persistentcy code in BLOAT thiss + // code has just been + // commented out and left untouched. --Arrin + + /* + * public void visitRCExpr(RCExpr expr) { expr.visitChildren(this); + * + * genPostponed(expr); // Move the rc forward as far as possible. // // For + * example, for the expression: // // rc(x).m(rc(a).b) // // we want to + * generate: // // aload x // aload a // rc 0 // getfield // rc 1 // + * invoke // // rather than: // // aload x // rc 0 // aload a // rc 0 // + * getfield // invoke // Instruction postpone = null; + * + * Node parent = expr.parent(); // If the parent is wrapped in a + * ZeroCheckExpr, then look at the // parent's parent. + * + * if(parent instanceof ZeroCheckExpr) parent = parent.parent(); // If the + * depth is going to be > 0, postpone the rc instruction // to just before + * the getfield, putfield, invoke, xaload, xastore, etc. // If the stack + * depth for the rc is going to be 0, the rc will (be) // the next + * instruction generated anyway, so don't postpone. + * + * if (parent instanceof ArrayRefExpr) { ArrayRefExpr p = (ArrayRefExpr) + * parent; + * + * if (expr == p.array()) { if (p.isDef()) { // a[i] := r // Stack at the + * xastore: ... a i r postpone = new Instruction(opcx_rc, new + * Integer(p.type().stackHeight() + 1)); } else { // use a[i] // Stack at + * the xaload: ... a i postpone = new Instruction(opcx_rc, new Integer(1)); } } } + * else if (parent instanceof CallMethodExpr) { CallMethodExpr p = + * (CallMethodExpr) parent; + * + * if (expr == p.receiver()) { // a.m(b, c) // Stack at the invoke: ... a b + * c MemberRef method = p.method(); int depth = + * method.nameAndType().type().stackHeight(); postpone = new + * Instruction(opcx_rc, new Integer(depth)); } } else if (parent instanceof + * FieldExpr) { FieldExpr p = (FieldExpr) parent; + * + * if (expr == p.object()) { if (p.isDef()) { // a.b := r // Stack at the + * putfield: ... a r postpone = new Instruction(opcx_rc, new + * Integer(p.type().stackHeight())); } } } + * + * if (postpone == null) { int depth = 0; + * + * if(expr.expr() instanceof StackExpr) { // If the rc works on a StackExpr, + * calculate its depth in the // stack. In all other cases, the rc will + * operate on whatever // is on top of the stack. StackExpr stackVar = + * (StackExpr) expr.expr(); depth = stackHeight - stackVar.index() - 1; } + * + * method.addInstruction(opcx_rc, new Integer(depth)); } else { + * postponedInstructions.put(parent, postpone); } } + */ + + /* + * public void visitUCExpr(UCExpr expr) { expr.visitChildren(this); + * + * if(true) return; + * + * genPostponed(expr); // Move the uc forward as far as possible. + * Instruction postpone = null; + * + * Node parent = expr.parent(); // If the depth is going to be > 0, postpone + * the uc instruction // to just before the putfield. If the stack depth for + * the // uc is going to be 0, the uc will the next instruction // generated + * anyway, so don't postpone. + * + * if (parent instanceof FieldExpr) { FieldExpr p = (FieldExpr) parent; + * + * if (expr == p.object()) { if (p.isDef()) { // a.b := r // Stack at the + * putfield: ... a r if (expr.kind() == UCExpr.POINTER) { postpone = new + * Instruction(opcx_aupdate, new Integer(p.type().stackHeight())); } else if + * (expr.kind() == UCExpr.SCALAR) { postpone = new Instruction(opcx_supdate, + * new Integer(p.type().stackHeight())); } else { throw new + * RuntimeException(); } } } } + * + * if (postpone == null) { int depth = 0; + * + * if(expr.expr() instanceof StackExpr) { // If the UCExpr operates on a + * stack variable, use that to // determine the depth. In all other cases, + * use 0. StackExpr stackVar = (StackExpr) expr.expr(); depth = stackHeight - + * stackVar.index() - 1; } + * + * if (expr.kind() == UCExpr.POINTER) { method.addInstruction(opcx_aupdate, + * new Integer(depth)); } else if (expr.kind() == UCExpr.SCALAR) { + * method.addInstruction(opcx_supdate, new Integer(depth)); } else { throw + * new RuntimeException(); } } else { postponedInstructions.put(parent, + * postpone); } } + */ + public void visitRetStmt(RetStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + // genPostponed(stmt); + + Subroutine sub = stmt.sub(); + Assert.isTrue(sub.returnAddress() != null); + lastAdded = method.getInstructionList().append(lastAdded, + new RET(sub.localVariable().getIndex())); + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + Type type = stmt.expr().type(); + + // Stack should be empty after return + lastAdded = method.getInstructionList().append(lastAdded, + InstructionFactory.createReturn(type)); + stackHeight = 0; + } + + public void visitReturnStmt(ReturnStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + // genPostponed(stmt); + + stmt.visitChildren(this); + lastAdded = method.getInstructionList().append(lastAdded, new RETURN()); + + // Stack height is zero after return + stackHeight = 0; + } + + public void visitStoreExpr(StoreExpr expr) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + expr); + } + + MemExpr lhs = expr.target(); + Expr rhs = expr.expr(); + + boolean returnsValue = !(expr.parent() instanceof ExprStmt); + + // eliminate the store if the both sides have the same target + + if (!returnsValue) { + if (lhs instanceof LocalExpr && rhs instanceof LocalExpr) { + + // The second condition checks that the right sides have indeed + // been stored-- otherwise (ie, if we're just keeping it on the + // stack), we should not eliminate this store + + if (((LocalExpr) lhs).index() == ((LocalExpr) rhs).index() + + && (!OPT_STACK || currentSO + .shouldStore(((LocalExpr) ((LocalExpr) rhs) + .def())))) { + return; + } + } + + // Special case to handle L := L + k. + // Generate "iinc L, k" instead of "iload L; ldc k; iadd; istore + // L". + // + // TODO: for L := M + k, generate "iload M; istore L; iinc L, + // k". + // + + /* + * This next special case was modified for stack optimization. If L + * is marked for a dup, the fact that its value is never on the + * stack means we don't have anything to dup-- we need to load + * instead. (Things get more complicated if it was marked for a + * dup_x2, but that's not likely to happen) + */ + + if (lhs instanceof LocalExpr && Assert.isIntegral(lhs.type())) { + Integer value = null; + // eliminate the store if the both sides have the same target + + int index = ((LocalExpr) lhs).index(); + + if (rhs instanceof ArithExpr) { + ArithExpr arith = (ArithExpr) rhs; + + Expr left = arith.left(); + Expr right = arith.right(); + + if (left instanceof LocalExpr + && index == ((LocalExpr) left).index() + && right instanceof ConstantExpr) { + + ConstantExpr c = (ConstantExpr) right; + + if (c.value() instanceof Integer) { + value = (Integer) c.value(); + } + + } else if (right instanceof LocalExpr + && index == ((LocalExpr) right).index() + && left instanceof ConstantExpr + && arith.operation() == ArithExpr.ADD) { + + // This will not work for x = c - x because it is not + // the + // same as x = x - c. + + ConstantExpr c = (ConstantExpr) left; + + if (c.value() instanceof Integer) { + value = (Integer) c.value(); + } + } + + if (value != null && arith.operation() == ArithExpr.SUB) { + value = new Integer(-value.intValue()); + } else if (arith.operation() != ArithExpr.ADD) { + value = null; + } + } + + if (value != null) { + int incr = value.intValue(); + + if (incr == 0) { + // No need to increment by 0. + + // for a better understanding of what's going on in + // these + // additions, see VisitLocalExpr, where we do basically + // the + // same thing. + + if (OPT_STACK) { + int dups, dup_x1s, dup_x2s; + dups = currentSO.dups((LocalExpr) lhs); + dup_x1s = currentSO.dup_x1s((LocalExpr) lhs); + dup_x2s = currentSO.dup_x2s((LocalExpr) lhs); + for (int i = 0; i < dup_x2s; i++) { + // This is really awful, but be consoled in that + // it is + // highly improbable to happen... this is just + // to make correct code in the chance that we + // have something like this. + lastAdded = method.getInstructionList().append( + lastAdded, new LDC(0)); + lastAdded = method.getInstructionList().append( + lastAdded, new DUP_X2()); + lastAdded = method.getInstructionList().append( + lastAdded, new POP()); + stackHeight += 1; + } + for (int i = 0; i < dup_x1s; i++) { + lastAdded = method.getInstructionList().append( + lastAdded, new LDC(0)); + lastAdded = method.getInstructionList().append( + lastAdded, new SWAP()); + stackHeight += 1; + } + for (int i = 0; i < dups; i++) { + lastAdded = method.getInstructionList().append( + lastAdded, new LDC(0)); + stackHeight += 1; + } + } + return; + + } else if ((short) incr == incr) { + // Only generate an iinc if the increment fits in + // a short. + // method.addLocalVariable("codeGenLocal" + index, + // Type.INT, index, null, null); + + lastAdded = method.getInstructionList().append( + lastAdded, new IINC(index, incr)); + + if (OPT_STACK) { + int dups, dup_x1s, dup_x2s; + dups = currentSO.dups((LocalExpr) lhs); + dup_x1s = currentSO.dup_x1s((LocalExpr) lhs); + dup_x2s = currentSO.dup_x2s((LocalExpr) lhs); + for (int i = 0; i < dup_x2s; i++) { + // method.addLocalVariable("codeGenLocal" + // + ((LocalExpr) lhs).index(), Type.INT, + // ((LocalExpr) lhs).index(), null, null); + lastAdded = method.getInstructionList().append( + lastAdded, + new ISTORE(((LocalExpr) lhs).index())); + lastAdded = method.getInstructionList().append( + lastAdded, new DUP_X2()); + lastAdded = method.getInstructionList().append( + lastAdded, new POP()); + stackHeight += 1; + } + for (int i = 0; i < dup_x1s; i++) { + // method.addLocalVariable("codeGenLocal" + // + ((LocalExpr) lhs).index(), Type.INT, + // ((LocalExpr) lhs).index(), null, null); + lastAdded = method.getInstructionList().append( + lastAdded, + new ILOAD(((LocalExpr) lhs).index())); + lastAdded = method.getInstructionList().append( + lastAdded, new SWAP()); + stackHeight += 1; + } + for (int i = 0; i < dups; i++) { + lastAdded = method.getInstructionList().append( + lastAdded, + new ILOAD(((LocalExpr) lhs).index())); + // method.addLocalVariable("codeGenLocal" + // + ((LocalExpr) lhs).index(), Type.INT, + // ((LocalExpr) lhs).index(), null, null); + + stackHeight += 1; + } + } + return; + } + } + } + } + + // Generate, and return the value. + lhs.visitChildren(this); + rhs.visit(this); + + if (returnsValue) { + if (lhs instanceof ArrayRefExpr) { + // array index rhs --> rhs array index rhs + if (rhs.type().getSize() == 2) { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X2()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X2()); + stackHeight += 1; + } + + } else if (lhs instanceof FieldExpr) { + // object rhs --> rhs object rhs + if (rhs.type().getSize() == 2) { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X1()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X1()); + stackHeight += 1; + } + + } else { + // rhs --> rhs rhs + if (rhs.type().getSize() == 2) { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP()); + stackHeight += 1; + } + } + } + + // genPostponed(expr); + lhs.visitOnly(this); + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + // genPostponed(stmt); + + Subroutine sub = stmt.sub(); + Assert.isTrue(sub.localVariable() != null); + method.getInstructionList().append( + new ASTORE(sub.localVariable().getIndex())); + stackHeight -= 1; + } + + public void visitJsrStmt(JsrStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + // genPostponed(stmt); + + Block entry = stmt.sub().entry(); + InstructionHandle entryIH = getLabelInstructionHandle(entry.label()); + + lastAdded = method.getInstructionList().append(lastAdded, + new JSR(entryIH)); + stackHeight += 1; + + if (stmt.follow() != next) { + InstructionHandle follow = getLabelInstructionHandle(stmt.follow() + .label()); + lastAdded = method.getInstructionList().append(lastAdded, + new GOTO(follow)); + } + } + + public void visitSwitchStmt(SwitchStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + InstructionHandle[] targets = new InstructionHandle[stmt.targets().length]; + InstructionHandle defaultTarget = getLabelInstructionHandle(stmt + .defaultTarget().label()); + for (int i = 0; i < targets.length; i++) { + targets[i] = getLabelInstructionHandle(stmt.targets()[i].label()); + } + + lastAdded = method.getInstructionList().append(lastAdded, + new SWITCH(stmt.values(), targets, defaultTarget)); + stackHeight -= 1; + } + + public void visitStackManipStmt(StackManipStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + // genPostponed(stmt); + + // All the children are stack variables, so don't so anything. + + switch (stmt.kind()) { + case StackManipStmt.SWAP: { + lastAdded = method.getInstructionList().append(lastAdded, + new SWAP()); + break; + } + case StackManipStmt.DUP: + lastAdded = method.getInstructionList() + .append(lastAdded, new DUP()); + stackHeight += 1; + break; + case StackManipStmt.DUP_X1: + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X1()); + stackHeight += 1; + break; + case StackManipStmt.DUP_X2: + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X2()); + stackHeight += 1; + break; + case StackManipStmt.DUP2: + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2()); + stackHeight += 2; + break; + case StackManipStmt.DUP2_X1: + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X1()); + stackHeight += 2; + break; + case StackManipStmt.DUP2_X2: + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X2()); + stackHeight += 2; + break; + } + } + + public void visitThrowStmt(ThrowStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + // genPostponed(stmt); + + lastAdded = method.getInstructionList().append(lastAdded, new ATHROW()); + } + + /* + * public void visitSCStmt(SCStmt stmt) { stmt.visitChildren(this); + * genPostponed(stmt); method.addInstruction(opcx_aswizzle); stackHeight -= + * 2; } + *//* + * public void visitSRStmt(SRStmt stmt) { stmt.visitChildren(this); + * genPostponed(stmt); method.addInstruction(opcx_aswrange); stackHeight -= + * 3; } + */ + public void visitArithExpr(ArithExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + int[][] stackChange = new int[][] { { -1, -2, -1, -2 }, + { -1, -2, 0, 0 }, { -1, -2, -1, -2 }, { -1, -2, -1, -2 }, + { -1, -2, 0, 0 }, { -1, -2, -1, -2 }, { -1, -2, -1, -2 }, + { -1, -2, 0, 0 }, { 0, -3, 0, 0 }, { 0, 0, -1, -3 }, + { 0, 0, -1, -3 } }; + + int type; + + if (Assert.isIntegral(expr.left().type())) { + type = 0; + + } else if (expr.left().type().equals(Type.LONG)) { + type = 1; + + } else if (expr.left().type().equals(Type.FLOAT)) { + type = 2; + + } else if (expr.left().type().equals(Type.DOUBLE)) { + type = 3; + + } else { + throw new IllegalArgumentException("Can't generate code for type: " + + expr.left().type() + " (expr " + expr + ")"); + } + + switch (expr.operation()) { + case ArithExpr.ADD: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IADD()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LADD()); + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FADD()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DADD()); + break; + } + stackHeight += stackChange[0][type]; + break; + case ArithExpr.AND: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IAND()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LAND()); + break; + case 2: + case 3: + break; + } + stackHeight += stackChange[1][type]; + break; + case ArithExpr.DIV: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IDIV()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LDIV()); + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FDIV()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DDIV()); + break; + } + stackHeight += stackChange[2][type]; + break; + case ArithExpr.MUL: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IMUL()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LMUL()); + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FMUL()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DMUL()); + break; + } + stackHeight += stackChange[3][type]; + break; + case ArithExpr.IOR: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IOR()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LOR()); + break; + case 2: + case 3: + break; + } + stackHeight += stackChange[4][type]; + break; + case ArithExpr.REM: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IREM()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LREM()); + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FREM()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DREM()); + break; + } + stackHeight += stackChange[5][type]; + break; + case ArithExpr.SUB: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new ISUB()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LSUB()); + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FSUB()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DSUB()); + break; + } + stackHeight += stackChange[6][type]; + break; + case ArithExpr.XOR: + switch (type) { + case 0: + lastAdded = method.getInstructionList().append(lastAdded, + new IXOR()); + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LXOR()); + break; + case 2: + case 3: + break; + } + stackHeight += stackChange[7][type]; + break; + case ArithExpr.CMP: + switch (type) { + case 0: + break; + case 1: + lastAdded = method.getInstructionList().append(lastAdded, + new LCMP()); + break; + case 2: + case 3: + break; + } + stackHeight += stackChange[8][type]; + break; + case ArithExpr.CMPL: + switch (type) { + case 0: + case 1: + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FCMPL()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DCMPL()); + break; + } + stackHeight += stackChange[9][type]; + break; + case ArithExpr.CMPG: + switch (type) { + case 0: + case 1: + break; + case 2: + lastAdded = method.getInstructionList().append(lastAdded, + new FCMPG()); + break; + case 3: + lastAdded = method.getInstructionList().append(lastAdded, + new DCMPG()); + break; + } + stackHeight += stackChange[10][type]; + break; + } + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + expr.visitChildren(this); + lastAdded = method.getInstructionList().append(lastAdded, + new ARRAYLENGTH()); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + + Instruction inst; + + if (expr.isDef()) { + + inst = InstructionFactory.createArrayStore(expr.elementType()); + stackHeight = stackHeight - 2 - expr.elementType().getSize(); + } else { + inst = InstructionFactory.createArrayLoad(expr.elementType()); + stackHeight = stackHeight - 2 + expr.elementType().getSize(); + } + + lastAdded = method.getInstructionList().append(lastAdded, inst); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + + Instruction inst; + String declaring = ""; + MethodRef meth = (MethodRef) expr.method(); + if (meth.declaringClass() instanceof ArrayType) { + declaring = meth.declaringClass().getSignature().replace('/', '.'); + } else { + declaring = ((ObjectType) meth.declaringClass()).getClassName(); + } + + String methName = meth.name(); + String signature = Type.getMethodSignature(meth.returnType(), meth + .paramTypes()); + if (expr.kind() == CallMethodExpr.VIRTUAL) { + int index = method.getConstantPool().lookupMethodref(declaring, + methName, signature); + inst = new INVOKEVIRTUAL(index); + } else if (expr.kind() == CallMethodExpr.NONVIRTUAL) { + int index = method.getConstantPool().lookupMethodref(declaring, + methName, signature); + inst = new INVOKESPECIAL(index); + } else if (expr.kind() == CallMethodExpr.INTERFACE) { + int index = method.getConstantPool().lookupInterfaceMethodref( + declaring, methName, signature); + inst = new INVOKEINTERFACE(index, meth.paramTypes().length + 1); + // +1 because it must include the 'this' argument as well + } else { + throw new IllegalArgumentException(); + } + + lastAdded = method.getInstructionList().append(lastAdded, inst); + + // Pop reciever object off stack + stackHeight -= 1; + + // Pop each parameter off stack + Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) { + stackHeight -= params[i].type().getSize(); + } + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + MethodRef meth = (MethodRef) expr.method(); + String declaring = meth.declaringClass().toString(); + String methName = meth.name(); + String signature = Type.getMethodSignature(meth.returnType(), meth + .paramTypes()); + int index = method.getConstantPool().lookupMethodref(declaring, + methName, signature); + lastAdded = method.getInstructionList().append(lastAdded, + new INVOKESTATIC(index)); + + // Pop each parameter off stack + Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) { + stackHeight -= params[i].type().getSize(); + } + } + + public void visitCastExpr(CastExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + if (expr.castType() instanceof ReferenceType) { + ConstantPoolGen cpg = method.getConstantPool(); + if (expr.castType() instanceof ArrayType) { + lastAdded = method.getInstructionList().append( + lastAdded, + new CHECKCAST(cpg.addArrayClass((ArrayType) expr + .castType()))); + return; + } else { + lastAdded = method.getInstructionList().append( + lastAdded, + new CHECKCAST(cpg + .addClass((ObjectType) expr.castType()))); + return; + } + } + int opType = expr.expr().type().getType(); + int castType = expr.castType().getType(); + + switch (opType) { + case Constants.T_BYTE: + case Constants.T_SHORT: + case Constants.T_CHAR: + case Constants.T_INT: + switch (castType) { + case Constants.T_BYTE: + lastAdded = method.getInstructionList().append(lastAdded, + new I2B()); + return; + case Constants.T_SHORT: + lastAdded = method.getInstructionList().append(lastAdded, + new I2S()); + return; + case Constants.T_CHAR: + lastAdded = method.getInstructionList().append(lastAdded, + new I2C()); + return; + case Constants.T_INT: + return; + case Constants.T_LONG: + lastAdded = method.getInstructionList().append(lastAdded, + new I2L()); + stackHeight += 1; + return; + case Constants.T_FLOAT: + lastAdded = method.getInstructionList().append(lastAdded, + new I2F()); + return; + case Constants.T_DOUBLE: + lastAdded = method.getInstructionList().append(lastAdded, + new I2D()); + stackHeight += 1; + return; + } + throw new IllegalArgumentException("Can't generate cast for type " + + expr.castType()); + // new Type(castType)); + + case Constants.T_LONG: + switch (castType) { + case Constants.T_BYTE: + lastAdded = method.getInstructionList().append(lastAdded, + new L2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2B()); + return; + case Constants.T_SHORT: + lastAdded = method.getInstructionList().append(lastAdded, + new L2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2S()); + return; + case Constants.T_CHAR: + lastAdded = method.getInstructionList().append(lastAdded, + new L2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2C()); + return; + case Constants.T_INT: + lastAdded = method.getInstructionList().append(lastAdded, + new L2I()); + stackHeight -= 1; + return; + case Constants.T_LONG: + return; + case Constants.T_FLOAT: + lastAdded = method.getInstructionList().append(lastAdded, + new L2F()); + stackHeight -= 1; + return; + case Constants.T_DOUBLE: + lastAdded = method.getInstructionList().append(lastAdded, + new L2D()); + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + expr.castType()); + // new Type(castType)); + + case Constants.T_FLOAT: + switch (castType) { + case Constants.T_BYTE: + lastAdded = method.getInstructionList().append(lastAdded, + new F2I()); + lastAdded = method.getInstructionList().append(lastAdded, + new I2B()); + return; + case Constants.T_SHORT: + lastAdded = method.getInstructionList().append(lastAdded, + new F2I()); + lastAdded = method.getInstructionList().append(lastAdded, + new I2S()); + return; + case Constants.T_CHAR: + lastAdded = method.getInstructionList().append(lastAdded, + new F2I()); + lastAdded = method.getInstructionList().append(lastAdded, + new I2C()); + return; + case Constants.T_INT: + lastAdded = method.getInstructionList().append(lastAdded, + new F2I()); + return; + case Constants.T_LONG: + lastAdded = method.getInstructionList().append(lastAdded, + new F2L()); + stackHeight += 1; + return; + case Constants.T_FLOAT: + return; + case Constants.T_DOUBLE: + lastAdded = method.getInstructionList().append(lastAdded, + new F2D()); + stackHeight += 1; + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + expr.castType()); + // new Type(castType)); + + case Constants.T_DOUBLE: + switch (castType) { + case Constants.T_BYTE: + lastAdded = method.getInstructionList().append(lastAdded, + new D2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2B()); + return; + case Constants.T_SHORT: + lastAdded = method.getInstructionList().append(lastAdded, + new D2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2S()); + return; + case Constants.T_CHAR: + lastAdded = method.getInstructionList().append(lastAdded, + new D2I()); + stackHeight -= 1; + lastAdded = method.getInstructionList().append(lastAdded, + new I2C()); + return; + case Constants.T_INT: + lastAdded = method.getInstructionList().append(lastAdded, + new D2I()); + stackHeight -= 1; + return; + case Constants.T_LONG: + lastAdded = method.getInstructionList().append(lastAdded, + new D2L()); + return; + case Constants.T_FLOAT: + lastAdded = method.getInstructionList().append(lastAdded, + new D2F()); + return; + case Constants.T_DOUBLE: + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + expr.castType()); + // new Type(castType)); + default: + throw new IllegalArgumentException("Can't generate cast from type " + + expr.expr().type()); + // new Type(castType)); + } + } + + public void visitConstantExpr(ConstantExpr expr) { + int index = -1; + ConstantPoolGen cpg = method.getConstantPool(); + expr.visitChildren(this); + Object value = expr.value(); + // genPostponed(expr); + Type type = expr.type(); + + if (type.equals(Type.NULL) || type.equals(Type.OBJECT) || value == null) { + lastAdded = method.getInstructionList().append(lastAdded, + new ACONST_NULL()); + } else { + lastAdded = method.getInstructionList().append(lastAdded, + instFactory.createConstant(value)); + } + if (CodeGenerator.DEBUG) + System.out.println("Adding to ConstantPool " + expr.type() + + " index: " + index + " size: " + cpg.getSize()); + stackHeight += expr.type().getSize(); + } + + public boolean nowb = false; + + public void visitFieldExpr(FieldExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + + if (expr.isDef()) { + + boolean UC = false; // Do we need an UC? + + // Look at the FieldExpr's object for a UCExpr + Node check = expr.object(); + while (check instanceof CheckExpr) { + if (check instanceof UCExpr) { + UC = true; + break; + } + + CheckExpr c = (CheckExpr) check; + check = c.expr(); + } + + // Do we need to perform the write barrier? + if (!UC && USE_PERSISTENT) { + /* + * System.out.println("Emitting a putfield_nowb in " + + * this.method.declaringClass().classInfo().name() + "." + + * this.method.name()); + */ + nowb = true; + + // I commented out the next line because it generated a compiler + // error, and I figured it was just about some unimportant + // persistance stuff --Tom + // method.addInstruction(opcx_putfield_nowb, expr.field()); + + } else { + int index = -1; + if (expr.field().declaringClass() instanceof ArrayType) { + index = method.getConstantPool().addFieldref( + expr.field().declaringClass().getSignature(), + expr.field().name(), + expr.field().type().getSignature()); + } else { + index = method.getConstantPool().addFieldref( + ((ObjectType) expr.field().declaringClass()) + .getClassName(), expr.field().name(), + expr.field().type().getSignature()); + } + lastAdded = method.getInstructionList().append(lastAdded, + new PUTFIELD(index)); + } + + stackHeight -= 1; // object + stackHeight -= expr.type().getSize(); + + } else { + int index = -1; + if (expr.field().declaringClass() instanceof ArrayType) { + index = method.getConstantPool() + .addFieldref( + expr.field().declaringClass().getSignature(), + expr.field().name(), + expr.field().type().getSignature()); + } else { + index = method.getConstantPool().addFieldref( + ((ObjectType) expr.field().declaringClass()) + .getClassName(), expr.field().name(), + expr.field().type().getSignature()); + } + lastAdded = method.getInstructionList().append(lastAdded, + new GETFIELD(index)); + stackHeight -= 1; // pop object + stackHeight += expr.type().getSize(); + } + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + ReferenceType checkType = (ReferenceType) expr.checkType(); + int index = -1; + if (checkType instanceof ArrayType) { + index = method.getConstantPool().addArrayClass( + (ArrayType) checkType); + } else { + index = method.getConstantPool().addClass((ObjectType) checkType); + } + lastAdded = method.getInstructionList().append(lastAdded, + new INSTANCEOF(index)); + } + + public void visitLocalExpr(LocalExpr expr) { + // genPostponed(expr); + + boolean cat2 = (expr.type().getSize() == 2); // how many stack + // positions does this take up? + + Instruction inst = null; + // assigned + // a real value + + if (OPT_STACK && DB_OPT_STACK) { + currentSO.infoDisplay(expr); + } + if (expr.isDef()) { + + if (!OPT_STACK || currentSO.shouldStore(expr)) { + inst = InstructionFactory + .createStore(expr.type(), expr.index()); + stackHeight = stackHeight - expr.type().getSize(); + } + } else { + if (OPT_STACK && currentSO.onStack(expr)) { // don't load if it's + // already on + // the stack + if (currentSO.shouldSwap(expr)) { + if (cat2) + throw new IllegalArgumentException( + "Can't swap for wide expression " + + expr.toString() + " of type " + + expr.type().toString()); + else { + inst = new SWAP(); + stackHeight -= 1; + } + } + } else { + inst = InstructionFactory.createLoad(expr.type(), expr.index()); + stackHeight = stackHeight + expr.type().getSize(); + } + } + + if (inst instanceof SWAP) + lastAdded = method.getInstructionList().append(lastAdded, inst); + // don't give a swap an operand + else if (inst != null && !(expr.isDef())) { + // if this is a load, we want + // the load before any dups. + lastAdded = method.getInstructionList().append(lastAdded, inst); + // method.addLocalVariable("codeGenLocal" + expr.index(), + // expr.type(), + // expr.index(), null, null); + + if (CodeGenerator.OPT_STACK_2)// remember the definition of + // this use + rememberDef(lastAdded, expr); + } + + if (OPT_STACK) { + // generate dups for this value on top of the stack + int dups, dup_x1s, dup_x2s; + dups = currentSO.dups(expr); + dup_x1s = currentSO.dup_x1s(expr); + dup_x2s = currentSO.dup_x2s(expr); + + for (int i = 0; i < dup_x2s; i++) + if (cat2) { // (cat2 is for wide types) + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X2()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X2()); + stackHeight += 1; + } + for (int i = 0; i < dup_x1s; i++) + if (cat2) { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2_X1()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP_X1()); + stackHeight += 1; + } + for (int i = 0; i < dups; i++) + if (cat2) { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP2()); + stackHeight += 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new DUP()); + stackHeight += 1; + } + } + + // if we have an opcode for a def (i.e., a store), generate it + if (inst != null && expr.isDef()) { + // method.addLocalVariable("codeGenLocal" + expr.index(), + // expr.type(), + // expr.index(), null, null); + + lastAdded = method.getInstructionList().append(lastAdded, inst); + + if (CodeGenerator.OPT_STACK_2) + rememberDef(lastAdded, expr); + } + + if (OPT_STACK // if we shouldn't store, + && !currentSO.shouldStore(expr)) { // an extra thing will + // be + if (cat2) { // on the stack. pop it + lastAdded = method.getInstructionList().append(lastAdded, + new POP2()); + stackHeight -= 2; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new POP()); + stackHeight -= 1; + } + } + // (if this leaves a useless dup/pop combination, let peephole fix it) + // method.addInstruction(opcode, new LocalVariable(expr.index())); + } + + public void visitNegExpr(NegExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + + if (Assert.isIntegral(expr.type())) { + lastAdded = method.getInstructionList().append(lastAdded, + new INEG()); + } else if (expr.type().equals(Type.FLOAT)) { + lastAdded = method.getInstructionList().append(lastAdded, + new FNEG()); + } else if (expr.type().equals(Type.LONG)) { + lastAdded = method.getInstructionList().append(lastAdded, + new LNEG()); + } else if (expr.type().equals(Type.DOUBLE)) { + lastAdded = method.getInstructionList().append(lastAdded, + new DNEG()); + } else { + throw new IllegalArgumentException("Can't generate code for type: " + + expr.type() + " (expr " + expr + ")"); + } + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + if (expr.elementType() instanceof ReferenceType) { + int index = 0; + if (expr.elementType() instanceof ObjectType) + index = method.getConstantPool().addClass( + (ObjectType) expr.elementType()); + else + index = method.getConstantPool().addArrayClass( + (ArrayType) expr.elementType()); + + lastAdded = method.getInstructionList().append(lastAdded, + new ANEWARRAY(index)); + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new NEWARRAY((BasicType) expr.elementType())); + } + } + + public void visitNewExpr(NewExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + int index = method.getConstantPool().addClass( + (ObjectType) expr.objectType()); + lastAdded = method.getInstructionList().append(lastAdded, + new NEW(index)); + stackHeight += 1; + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + int index = 0; + if (expr.elementType() instanceof ObjectType) + index = method.getConstantPool().addClass( + (ObjectType) expr.elementType()); + else + index = method.getConstantPool().addArrayClass( + (ArrayType) expr.elementType()); + + lastAdded = method.getInstructionList().append(lastAdded, + new MULTIANEWARRAY(index, (short) expr.dimensions().length)); + stackHeight -= expr.dimensions().length; + stackHeight += 1; + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + // genPostponed(expr); + } + + public void visitShiftExpr(ShiftExpr expr) { + expr.visitChildren(this); + + // genPostponed(expr); + + if (Assert.isIntegral(expr.type())) { + if (expr.dir() == ShiftExpr.LEFT) { + lastAdded = method.getInstructionList().append(lastAdded, + new ISHL()); + stackHeight -= 1; + } else if (expr.dir() == ShiftExpr.RIGHT) { + lastAdded = method.getInstructionList().append(lastAdded, + new ISHR()); + stackHeight -= 1; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new IUSHR()); + stackHeight -= 1; + } + } else if (expr.type().equals(Type.LONG)) { + if (expr.dir() == ShiftExpr.LEFT) { + lastAdded = method.getInstructionList().append(lastAdded, + new LSHL()); + stackHeight -= 1; + } else if (expr.dir() == ShiftExpr.RIGHT) { + lastAdded = method.getInstructionList().append(lastAdded, + new LSHR()); + stackHeight -= 1; + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new LUSHR()); + stackHeight -= 1; + } + } else { + throw new IllegalArgumentException("Can't generate code for type: " + + expr.type() + " (expr " + expr + ")"); + } + } + + public void visitDefExpr(DefExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + } + + public void visitCatchExpr(CatchExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + } + + public void visitStackExpr(StackExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + int index = method.getConstantPool().addFieldref( + ((ObjectType) expr.field().declaringClass()).getClassName(), + expr.field().name(), expr.field().type().getSignature()); + + if (expr.isDef()) { + lastAdded = method.getInstructionList().append(lastAdded, + new PUTSTATIC(index)); + stackHeight -= expr.type().getSize(); + } else { + lastAdded = method.getInstructionList().append(lastAdded, + new GETSTATIC(index)); + stackHeight += expr.type().getSize(); + } + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + expr.visitChildren(this); + // genPostponed(expr); + } + + private void rememberDef(InstructionHandle ih, LocalExpr expr) { + Node def = expr.def(); + if (def != null) + ih.addAttribute("Def", def); + } +} diff --git a/src/edu/purdue/cs/bloat/codegen/Liveness.java b/src/edu/purdue/cs/bloat/codegen/Liveness.java new file mode 100644 index 0000000..6bb6f86 --- /dev/null +++ b/src/edu/purdue/cs/bloat/codegen/Liveness.java @@ -0,0 +1,823 @@ +/* + * Class: Liveness + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.codegen; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Handler; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MemRefExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.Graph; +import edu.purdue.cs.bloat.util.GraphNode; +import edu.purdue.cs.bloat.util.ImmutableIterator; + +/** + * Liveness represents the interference graph of the local variables contained + * in a control flow graph. + * + * When the liveness of two variables overlap each other, the two variables are + * said to interfere with each other. The interference graph represents + * this relationship between variables. There is an (un-directed) edge between + * variables a and b in the interference graph if variable + * a interferes with variable b. + */ +public class Liveness { + public static boolean DEBUG = false; + + public static boolean UNIQUE = false; + + public static final boolean BEFORE = false; + + public static final boolean AFTER = true; + + FlowGraph cfg; + + Graph ig; + + /** + * Constructor. + * + * @param cfg + * Control flow graph on which to perform liveness analysis. + */ + public Liveness(FlowGraph cfg) { + this.cfg = cfg; + computeIntersections(); + } + + /** + * Removes a local expression from the interference graph. + */ + public void removeVar(LocalExpr expr) { + ig.removeNode(expr); + } + + /** + * Should not be called. + */ + public boolean liveAtUse(final VarExpr isLive, final VarExpr at, + final boolean after) { + throw new RuntimeException(); + } + + /** + * Should not be called. + */ + public boolean liveAtStartOfBlock(VarExpr isLive, Block block) { + throw new RuntimeException(); + } + + /** + * Should not be called. + */ + public boolean liveAtEndOfBlock(VarExpr isLive, Block block) { + throw new RuntimeException(); + } + + /** + * Returns the LocalExprs (variables) that occur in the CFG. + * They correspond to nodes in the interference graph. + */ + public Collection defs() { + return ig.keySet(); + } + + /** + * Returns an Iterator of LocalExprs that interfere + * with a given VarExpr. + */ + public Iterator intersections(VarExpr a) { + Assert.isTrue(a != null, "Cannot get intersections for null def"); + Assert.isTrue(a.isDef(), "Cannot get intersections for variable uses"); + + final GraphNode node = ig.getNode(a); + + Assert.isTrue(node != null, "Cannot find IG node for " + a); + + return new Iterator() { + Iterator succs = ig.succs(node).iterator(); + + public boolean hasNext() { + return succs.hasNext(); + } + + public Object next() { + IGNode next = (IGNode) succs.next(); + return next.def; + } + + public void remove() { + throw new RuntimeException(); + } + }; + } + + /** + * Determines whether or not two variables interfere with one another. + */ + public boolean liveRangesIntersect(VarExpr a, VarExpr b) { + Assert.isTrue(a != null && b != null, + "Cannot get intersections for null def"); + Assert.isTrue(a.isDef() && b.isDef(), + "Cannot get intersections for variable uses"); + + if (a == b) { + return false; + } + + // If all locals should have unique colors, return true. + if (UNIQUE) { + return true; + } + + IGNode na = (IGNode) ig.getNode(a); + IGNode nb = (IGNode) ig.getNode(b); + + Assert.isTrue(na != null && nb != null); + + return ig.hasEdge(na, nb); + } + + /** + * Constructs the interference graph. + */ + private void computeIntersections() { + ig = new Graph(); // The interference graph + + if (Liveness.DEBUG) { + System.out.println("-----------Computing live ranges-----------"); + } + + // All of the nodes (IGNodes) in the IG + final List defNodes = new ArrayList(); + + // The IGNodes whose local variable is defined by a PhiCatchStmt + final List phiCatchNodes = new ArrayList(); + + // An array of NodeInfo for each node in the CFG (indexed by the + // node's pre-order index). Gives information about the local + // variables (nodes in the IG) that are defined in each block. + // The NodeInfos are stored in reverse order. That is, the + // NodeInfo for the final variable occurrence in the block is the + // first element in the list. + final List[] nodes = new ArrayList[cfg.size()]; + + // We need to keep track of the order of the statements in which + // variables occur. There is an entry in nodeIndices for each + // block in the CFG. Each entry consists of a mapping between a + // statement in which a variable occurs and the number of the + // statement (with respect to the other statements in which + // variables occur) of interest. This is hard to explain in + // words. This numbering comes into play in the liveOut method. + final Map[] nodeIndices = new LinkedHashMap[cfg.size()]; + + Iterator iter = cfg.nodes().iterator(); + + // Initialize nodes and nodeIndices + while (iter.hasNext()) { + Block block = (Block) iter.next(); + int blockIndex = cfg.preOrderIndex(block); + nodes[blockIndex] = new ArrayList(); + nodeIndices[blockIndex] = new LinkedHashMap(); + } + + // Go in trace order. Code generation for phis in the presence of + // critical edges depends on it! + + iter = cfg.trace().iterator(); + + // When performing liveness analysis, we traverse the tree from + // the bottom up. That is, we do a REVERSE traversal. + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + + block.visit(new TreeVisitor(TreeVisitor.REVERSE) { + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + if (!(stmt.target() instanceof LocalExpr)) { + return; + } + + LocalExpr target = (LocalExpr) stmt.target(); + + // Examine each predacessor and maintain some + // information + // about the definitions. Remember that we're dealing + // with + // a PhiJoinStmt. The predacessors of PhiJoinStmts are + // statements that define or use the local (SSA) + // variable. + Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + int predIndex = cfg.preOrderIndex(pred); + + List n = nodes[predIndex]; + Map indices = nodeIndices[predIndex]; + + indices.put(stmt, new Integer(n.size())); + NodeInfo info = new NodeInfo(stmt); + n.add(info); + + // Make a new node in the interference graph for target, + // if one does not already exists + IGNode node = (IGNode) ig.getNode(target); + + if (node == null) { + node = new IGNode(target); + ig.addNode(target, node); + defNodes.add(node); + } + + info.defNodes.add(node); + } + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + } + + public void visitStmt(Stmt stmt) { + } + }); + } + + iter = cfg.trace().iterator(); + + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + final int blockIndex = cfg.preOrderIndex(block); + + block.visit(new TreeVisitor(TreeVisitor.REVERSE) { + Node parent = null; + + public void visitNode(Node node) { + Node p = parent; + parent = node; + node.visitChildren(this); + parent = p; + } + + public void visitLocalExpr(LocalExpr expr) { + Assert.isTrue(parent != null); + + // Recall that a LocalExpr represents a use or a + // definition + // of a local variable. If the LocalExpr is defined by a + // PhiJoinStmt, the block in which it resides should + // already + // have some information about it. + + NodeInfo info; + + List n = nodes[blockIndex]; + Map indices = nodeIndices[blockIndex]; + + Integer i = (Integer) indices.get(parent); + + if (i == null) { + if (DEBUG) { + System.out.println("adding " + parent + " at " + + n.size()); + } + + indices.put(parent, new Integer(n.size())); + info = new NodeInfo(parent); + n.add(info); + + } else { + if (DEBUG) { + System.out.println("found " + parent + " at " + i); + } + + info = (NodeInfo) n.get(i.intValue()); + Assert.isTrue(info != null); + } + + if (expr.isDef()) { + IGNode node = (IGNode) ig.getNode(expr); + + if (node == null) { + node = new IGNode(expr); + ig.addNode(expr, node); + defNodes.add(node); + } + + info.defNodes.add(node); + } + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + NodeInfo info; + + List n = nodes[blockIndex]; + Map indices = nodeIndices[blockIndex]; + + Integer i = (Integer) indices.get(stmt); + + if (i == null) { + if (DEBUG) { + System.out.println("adding " + stmt + " at " + + n.size()); + } + + indices.put(stmt, new Integer(n.size())); + info = new NodeInfo(stmt); + n.add(info); + + } else { + if (DEBUG) { + System.out.println("found " + parent + " at " + i); + } + + info = (NodeInfo) n.get(i.intValue()); + Assert.isTrue(info != null); + } + + LocalExpr target = (LocalExpr) stmt.target(); + + IGNode node = (IGNode) ig.getNode(target); + + if (node == null) { + node = new IGNode(target); + ig.addNode(target, node); + defNodes.add(node); + phiCatchNodes.add(node); + } + + info.defNodes.add(node); + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + } + }); + } + + // Iterate over all of the nodes in the IG + int numDefs = defNodes.size(); + + for (int i = 0; i < numDefs; i++) { + IGNode node = (IGNode) defNodes.get(i); + LocalExpr def = node.def; + + // Set of blocks where this variable is live out (i.e. live on + // any of the block's outgoing edges). + BitSet m = new BitSet(cfg.size()); + + Iterator uses = def.uses().iterator(); + + // Look at each use of the local variable + while (uses.hasNext()) { + LocalExpr use = (LocalExpr) uses.next(); + Node parent = use.parent(); + + if (parent instanceof MemRefExpr + && ((MemRefExpr) parent).isDef()) { + parent = parent.parent(); + } + + // Skip catch-phis. We handle this later. + if (parent instanceof PhiCatchStmt) { + // If we want to be less conservative: + // Need to search back from the operand from all + // points in the protected region where it is live + // back to the def of the operand. For each block + // in protected region, if the operand def is closest + // dominator of the block + continue; + } + + if (DEBUG) { + System.out.println("searching for " + def + " from " + + parent); + } + + Block block = parent.block(); + + if (parent instanceof PhiJoinStmt) { + PhiJoinStmt phi = (PhiJoinStmt) parent; + + // The local variable (LocalExpr) occurs within a + // PhiJoinStmt. Look at the predacessors of the + // PhiJoinStmt. Recall that each predacessor defines one + // of + // the operands to the PhiJoinStmt. Locate the block + // that + // defines the LocalExpr in question. Call liveOut to + // determine for which nodes the LocalExpr is live out. + + // Examine the predacessors of the block containing the + // LocalExpr + Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + + if (phi.operandAt(pred) == use) { + Map indices = nodeIndices[cfg.preOrderIndex(pred)]; + Integer index = (Integer) indices.get(parent); + Assert.isTrue(index != null, "No index for " + + parent); + + liveOut(m, nodes, pred, index.intValue(), node, + phiCatchNodes); + break; + } + } + + } else { + // The LocalExpr is define in a non-Phi statement. + // Figure + // out which number definition define the LocalExpr in + // quest + // and call liveOut to compute the set of block in which + // the + // LocalExpr is live out. + + Map indices = nodeIndices[cfg.preOrderIndex(block)]; + Integer index = (Integer) indices.get(parent); + Assert.isTrue(index != null, "No index for " + parent); + + liveOut(m, nodes, block, index.intValue(), node, + phiCatchNodes); + } + } + } + + // Go through all of the variables that are defined by + // PhiCatchStmts and make them (the variables) conflict with + // everything that the operands of the PhiCatchStmt conflict + // with. See liveOut for a discussion. + + int numPhiCatches = phiCatchNodes.size(); + + for (int i = 0; i < numPhiCatches; i++) { + IGNode node = (IGNode) phiCatchNodes.get(i); + + PhiCatchStmt phi = (PhiCatchStmt) node.def.parent(); + + Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + LocalExpr operand = (LocalExpr) operands.next(); + LocalExpr def = (LocalExpr) operand.def(); + + if (def != null) { + IGNode opNode = (IGNode) ig.getNode(def); + + // Conflict with everything the operand conflicts with. + Iterator edges = new ImmutableIterator(ig.succs(opNode)); + + while (edges.hasNext()) { + IGNode otherNode = (IGNode) edges.next(); + + if (otherNode != node) { + if (DEBUG) { + System.out.println(otherNode.def + + " conflicts with " + opNode.def + + " and thus with " + node.def); + } + + ig.addEdge(otherNode, node); + ig.addEdge(node, otherNode); + } + } + } + } + } + + if (DEBUG) { + System.out.println("Interference graph ="); + System.out.println(ig); + } + } + + /** + * Computes (a portion of) the "live out" set for a given local variable. If + * a variable is live on a block's outgoing edge in the CFG, then it is + * "live out" at that block. + * + * @param m + * Bit vector that indicates the block for which block the + * defNode is live out + * @param nodes + * The NodeInfo for the local variables used or defined in each + * block + * @param block + * The block in which the LocalExpr of interest is defined + * @param nodeIndex + * Which number definition in the defining block + * @param defNode + * The node in the IG whose live out set we are interested in + * @param phiCatchNode + * The nodes in the interference graph that represent local + * variables defined by PhiCatchStmts + */ + // Nate sez: + // + // In a PhiJoin pred, add + // ... + // phi-target := phi-operand + // jump with throw succs + // + // Don't kill Phi targets in protected blocks + // The phi target and operand don't conflict + void liveOut(BitSet m, List[] nodes, Block block, int nodeIndex, + IGNode defNode, Collection phiCatchNodes) { + boolean firstNode = true; + + int blockIndex = cfg.preOrderIndex(block); + + ArrayList stack = new ArrayList(); + + Pos pos = new Pos(); + pos.block = block; + pos.blockIndex = blockIndex; + pos.nodeIndex = nodeIndex; + + stack.add(pos); + + while (!stack.isEmpty()) { + pos = (Pos) stack.remove(stack.size() - 1); + + block = pos.block; + blockIndex = pos.blockIndex; + nodeIndex = pos.nodeIndex; + + if (DEBUG) { + System.out.println(defNode.def + " is live at position " + + nodeIndex + " of " + block); + } + + boolean stop = false; + + // The nodes are sorted in reverse. So, the below gets all of + // the nodes defined at this block after nodeIndex. I believe + // this is an optimization so we don't calculate things twice. + // Or maybe its how we get things to terminate. + ListIterator iter = nodes[blockIndex].listIterator(nodeIndex); + + while (!stop && iter.hasNext()) { + NodeInfo info = (NodeInfo) iter.next(); + + if (DEBUG) { + System.out + .println(defNode.def + " is live at " + info.node); + } + + if (firstNode) { + // We don't care about the definition in the block that + // defines the LocalExpr of interest. + firstNode = false; + continue; + } + + // Look at all (?) of the definitions of the LocalExpr + Iterator e = info.defNodes.iterator(); + + while (e.hasNext()) { + IGNode node = (IGNode) e.next(); + + Iterator catchPhis = phiCatchNodes.iterator(); + + // Calculating the live region of the target of a + // phi-catch + // node is a little tricky. The target (variable) must + // be + // live throughout the protected region as well as after + // the + // PhiCatchStmt (its definition). However, we do not + // want + // the phi-catch target to conflict (interfere) with any + // of + // its operands. So, we make the target conflict with + // all + // of the variables that its operand conflict with. See + // page 37 of Nate's Thesis. + + PHIS: while (catchPhis.hasNext()) { + IGNode catchNode = (IGNode) catchPhis.next(); + + PhiCatchStmt phi = (PhiCatchStmt) catchNode.def + .parent(); + + Handler handler = (Handler) cfg.handlersMap().get( + phi.block()); + + Assert.isTrue(handler != null, "Null handler for " + + phi.block()); + + if (handler.protectedBlocks().contains(block)) { + Iterator operands = phi.operands().iterator(); + + // If the block containing the LocalExpr in + // question + // resides inside a protected region. Make sure + // that + // the LocalExpr is not one of the operands to + // the + // PhiCatchStmt associated with the protected + // region. + + while (operands.hasNext()) { + LocalExpr expr = (LocalExpr) operands.next(); + + if (expr.def() == node.def) { + continue PHIS; + } + } + + if (DEBUG) { + System.out.println(defNode.def + + " conflicts with " + node.def); + } + + // Hey, wow. The variable defined in the + // phi-catch + // interferes with the variable from the + // worklist. + ig.addEdge(node, catchNode); + ig.addEdge(catchNode, node); + } + } + + if (node != defNode) { + if (DEBUG) { + System.out.println(defNode.def + " conflicts with " + + node.def); + } + + // If the node in the worklist is not the node we + // started + // with, then they conflict. + ig.addEdge(node, defNode); + ig.addEdge(defNode, node); + + } else { + if (DEBUG) { + System.out.println("def found stopping search"); + } + + // We've come across a definition of the LocalExpr in + // question, so we don't need to do any more. + stop = true; + } + } + } + + if (!stop) { + // Propagate the liveness to each of the predacessors of the + // block in which the variable of interest is defined. This + // is accomplished by setting the appropriate bit in m. We + // also add another Pos to the worklist to work on the + // predacessor block. + Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + int predIndex = cfg.preOrderIndex(pred); + + if (DEBUG) { + System.out.println(defNode.def + " is live at end of " + + pred); + } + + if (!m.get(predIndex)) { + pos = new Pos(); + pos.block = pred; + pos.blockIndex = predIndex; + + // Look at all of the statements in which a variable + // occur + pos.nodeIndex = 0; + + m.set(predIndex); + stack.add(pos); + } + } + } + } + } + + /** + * Represents a node in the interference graph. Connected nodes in the + * interference graph interfere with each other. That is, their live regions + */ + class IGNode extends GraphNode { + LocalExpr def; + + /** + * Constructor. + * + * @param def + * The local variable represented by this node. + */ + public IGNode(LocalExpr def) { + this.def = def; + } + + public String toString() { + return def.toString(); + } + } + + /** + * Stores information about each Node in an expression tree (!) that defines + * a local variable (i.e. PhiJoinStmt, PhiCatchStmt, and the parent of a + * LocalExpr). + */ + class NodeInfo { + Node node; // Node in an expression tree in which a variable occurs + + List defNodes; // node(s) in IG that define above Node + + public NodeInfo(Node node) { + this.node = node; + defNodes = new ArrayList(); + } + } + + class Key { + int blockIndex; + + Node node; + + public Key(Node node, int blockIndex) { + this.blockIndex = blockIndex; + this.node = node; + } + + public int hashCode() { + return node.hashCode() ^ blockIndex; + } + + public boolean equals(Object obj) { + if (obj instanceof Key) { + Key key = (Key) obj; + return key.node == node && key.blockIndex == blockIndex; + } + + return false; + } + } + + /** + * A Pos is an element in the worklist used to determine the live out set of + * a given LocalExpr. It consists of the block in which a local variable + * definition occurs, the block's index (i.e. pre-order traversal number) in + * the CFG, and the number of the definition in the block that defines the + * LocalExpr of interest. + */ + class Pos { + Block block; + + int blockIndex; + + int nodeIndex; + } +} diff --git a/src/edu/purdue/cs/bloat/codegen/Makefile b/src/edu/purdue/cs/bloat/codegen/Makefile new file mode 100644 index 0000000..57fb62d --- /dev/null +++ b/src/edu/purdue/cs/bloat/codegen/Makefile @@ -0,0 +1,27 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + CodeGenerator.class\ + Liveness.class\ + RegisterAllocator.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/codegen/RegisterAllocator.java b/src/edu/purdue/cs/bloat/codegen/RegisterAllocator.java new file mode 100644 index 0000000..14fd75f --- /dev/null +++ b/src/edu/purdue/cs/bloat/codegen/RegisterAllocator.java @@ -0,0 +1,752 @@ +/* + * Class: RegisterAllocator + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.codegen; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.Graph; +import edu.purdue.cs.bloat.util.GraphNode; +import edu.purdue.cs.bloat.optimize.Optimization; + +import java.util.*; + +/** + * RegisterAllocator performs analysis on a control flow graph and determines + * the minimum amount of local variables needed in a method. + * + * @see LocalVariable + */ +// Note that RegisterAllocator uses a different IGNode from Liveness! +public class RegisterAllocator implements Optimization { + FlowGraph cfg; + + Liveness liveness; + + Map colors; + + int colorsUsed; + + final static float MAX_WEIGHT = Float.MAX_VALUE; + + final static float LOOP_FACTOR = 10.0F; + + final static int MAX_DEPTH = (int) (Math.log(MAX_WEIGHT) / Math + .log(LOOP_FACTOR)); + + public void transform(MethodState state) { + transform(state.cfg(), new Liveness(state.cfg())); + } + + public String traceMessage(String dateString) { + return ""; + } + + /* + * Print a debugging message. Called before #transform(). + * + * @see edu.purdue.cs.bloat.optimize.Optimization#preDebugMessage() + */ + public String preDebugMessage() { + return ""; + } + + /* + * Print a debugging message. Called after #transform(). + * + * @see edu.purdue.cs.bloat.optimize.Optimization#postDebugMessage() + */ + public String postDebugMessage() { + return ""; + } + + /** + * Constructor. Builds an interference graph based on the expression nodes + * found in liveness. Traverses the graph and determines which nodes needs + * to be precolored and which nodes can be coalesced (move statements). + * Nodes are coalesced and local variables are assigned to expressions. + * + * @see FlowGraph + * @see LocalVariable + */ + public void transform(FlowGraph cfg, Liveness liveness) { + this.cfg = cfg; + this.liveness = liveness; + colorsUsed = 0; + colors = new LinkedHashMap(); + + // Construct the interference graph. + final Graph ig = new Graph(); + + Iterator iter = liveness.defs().iterator(); + + while (iter.hasNext()) { + VarExpr def = (VarExpr) iter.next(); + + if (!(def instanceof LocalExpr)) { + // Ignore node in the Liveness IG that are not LocalExprs + continue; + } + + // Create a new node in the IG, if one does not already exist + IGNode defNode = (IGNode) ig.getNode(def); + + if (defNode == null) { + defNode = new IGNode((LocalExpr) def); + ig.addNode(def, defNode); + } + + // Examine each variable that interferes with def + Iterator intersections = liveness.intersections(def); + + while (intersections.hasNext()) { + VarExpr expr = (VarExpr) intersections.next(); + + if (expr == def) { + // If for some reason, def interferes with itself, + // ignore it + continue; + } + + // Add an edge in RegisterAllocator's IG between the variables + // that interfere + if (expr instanceof LocalExpr) { + IGNode node = (IGNode) ig.getNode(expr); + + if (node == null) { + node = new IGNode((LocalExpr) expr); + ig.addNode(expr, node); + } + + ig.addEdge(defNode, node); + ig.addEdge(node, defNode); + } + } + } + + // Arrays of expressions that invovle a copy of one local variable + // to another. Expressions invovled in copies (i.e. "moves") can + // be coalesced into one expression. + // MODIFIED + + final ArrayList copies = new ArrayList(); + + // Nodes that are the targets of InitStmt are considered to be + // precolored. + final ArrayList precolor = new ArrayList(); + + cfg.visit(new TreeVisitor() { + /* + */ + public void visitBlock(Block block) { + // Don't visit the sink block. There's nothing interesting + // there. + if (block != RegisterAllocator.this.cfg.sink()) { + block.visitChildren(this); + } + } + + public void visitPhiStmt(PhiStmt stmt) { + stmt.visitChildren(this); + + if (!(stmt.target() instanceof LocalExpr)) { + return; + } + + // A PhiStmt invovles an assignment (copy). So note the copy + // between the target and all of the PhiStmt's operands in the + // copies list. + + IGNode lnode = (IGNode) ig.getNode(stmt.target()); + + HashSet set = new LinkedHashSet(); + + Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + Expr op = (Expr) e.next(); + + if (op instanceof LocalExpr && op.def() != null) { + if (!set.contains(op.def())) { + set.add(op.def()); + + if (op.def() != stmt.target()) { + IGNode rnode = (IGNode) ig.getNode(op.def()); + copies.add(new IGNode[] { lnode, rnode }); + } + } + } + } + } + + public void visitStoreExpr(StoreExpr expr) { + expr.visitChildren(this); + + if (!(expr.target() instanceof LocalExpr)) { + return; + } + + IGNode lnode = (IGNode) ig.getNode(expr.target()); + + if (expr.expr() instanceof LocalExpr + && expr.expr().def() != null) { + + // A store of a variable into another variable is a copy + IGNode rnode = (IGNode) ig.getNode(expr.expr().def()); + copies.add(new IGNode[] { lnode, rnode }); + return; + } + + // Treat L := L + k as a copy so that they get converted + // back to iincs. + if (expr.target().type().equals(Type.INT)) { + if (!(expr.expr() instanceof ArithExpr)) { + return; + } + + // We're dealing with integer arithmetic. Remember that + // an + // ArithExpr has a left and a right operand. If one of + // the + // operands is a variable and if the other is a constant + // and + // the operation is addition or substraction, we have an + // increment. + + ArithExpr rhs = (ArithExpr) expr.expr(); + LocalExpr var = null; + + Integer value = null; + + if (rhs.left() instanceof LocalExpr + && rhs.right() instanceof ConstantExpr) { + + var = (LocalExpr) rhs.left(); + + ConstantExpr c = (ConstantExpr) rhs.right(); + + if (c.value() instanceof Integer) { + value = (Integer) c.value(); + } + + } else if (rhs.right() instanceof LocalExpr + && rhs.left() instanceof ConstantExpr) { + + var = (LocalExpr) rhs.right(); + + ConstantExpr c = (ConstantExpr) rhs.left(); + + if (c.value() instanceof Integer) { + value = (Integer) c.value(); + } + } + + if (rhs.operation() == ArithExpr.SUB) { + if (value != null) { + value = new Integer(-value.intValue()); + } + + } else if (rhs.operation() != ArithExpr.ADD) { + value = null; + } + + if (value != null && var.def() != null) { + int incr = value.intValue(); + + if ((short) incr == incr) { + // Only generate an iinc if the increment + // fits in a short + IGNode rnode = (IGNode) ig.getNode(var.def()); + copies.add(new IGNode[] { lnode, rnode }); + } + } + } + } + + /* + */ + public void visitInitStmt(InitStmt stmt) { + stmt.visitChildren(this); + + // The initialized variables are precolored. + LocalExpr[] t = stmt.targets(); + + for (int i = 0; i < t.length; i++) { + precolor.add(t[i]); + } + } + }); + + // Coalesce move related nodes, maximum weight first. + /* + */ + + while (copies.size() > 0) { + // We want the copy (v <- w) with the maximum: + // weight(v) + weight(w) + // --------------------- + // size(union) + // where union is the intersection of the nodes that conflict + // with v and the nodes that conflict with w. This equation + // appears to be in conflict with the one given on page 38 of + // Nate's thesis. + + HashSet union; // The union of neighboring nodes + + int max = 0; + + IGNode[] copy = (IGNode[]) copies.get(max); + + float maxWeight = copy[0].weight + copy[1].weight; + union = new LinkedHashSet(); + union.addAll(ig.succs(copy[0])); + union.addAll(ig.succs(copy[1])); + maxWeight /= union.size(); + + for (int i = 1; i < copies.size(); i++) { + copy = (IGNode[]) copies.get(i); + + float weight = copy[0].weight + copy[1].weight; + union.clear(); + union.addAll(ig.succs(copy[0])); + union.addAll(ig.succs(copy[1])); + weight /= union.size(); + + if (weight > maxWeight) { + // The ith copy has the maximum weight + maxWeight = weight; + max = i; + } + } + + // Remove the copy with the max weight from the copies list. He + // does it in a rather round-about way. + copy = (IGNode[]) copies.get(max); + copies.set(max, copies.get(copies.size() - 1)); + copies.remove(copies.size() - 1); + + if (!ig.hasEdge(copy[0], copy[1])) { + // If the variables involved in the copy do not interfere with + // each other, they are coalesced. + + if (CodeGenerator.DEBUG) { + System.out.println("coalescing " + copy[0] + " " + copy[1]); + System.out.println(" 0 conflicts " + ig.succs(copy[0])); + System.out.println(" 1 conflicts " + ig.succs(copy[1])); + } + + ig.succs(copy[0]).addAll(ig.succs(copy[1])); + ig.preds(copy[0]).addAll(ig.preds(copy[1])); + + copy[0].coalesce(copy[1]); + + if (CodeGenerator.DEBUG) { + System.out.println(" coalesced " + copy[0]); + System.out.println(" conflicts " + ig.succs(copy[0])); + } + + // Remove coalesced node from the IG + ig.removeNode(copy[1].key); + + iter = copies.iterator(); + + // Examine all copies. If the copy involves the node that was + // coalesced, the copy is no longer interesting. Remove it. + while (iter.hasNext()) { + IGNode[] c = (IGNode[]) iter.next(); + + if (c[0] == copy[1] || c[1] == copy[1]) { + iter.remove(); + } + } + } + } + /* + */ + // Create a list of uncolored nodes. + ArrayList uncoloredNodes = new ArrayList(); + + Iterator nodes = ig.nodes().iterator(); + + while (nodes.hasNext()) { + IGNode node = (IGNode) nodes.next(); + + ArrayList p = new ArrayList(precolor); + p.retainAll(node.defs); + + // See if any node got coalesced with a precolored node. + if (p.size() == 1) { + // Precolored + node.color = ((LocalExpr) p.get(0)).index(); + + if (CodeGenerator.DEBUG) { + System.out.println("precolored " + node + " " + node.color); + } + + } else if (p.size() == 0) { + // Uncolored (i.e. not coalesced with any of the pre-colored + // nodes. + node.color = -1; + uncoloredNodes.add(node); + + } else { + // If two or more pre-colored nodes were coalesced, we have a + // problem. + throw new RuntimeException("coalesced pre-colored defs " + p); + } + } + + // MOD + /* + * Iterator inits = precolor.iterator(); while (inits.hasNext()) { + * VarExpr def = (VarExpr) inits.next(); IGNode defNode = (IGNode) + * ig.getNode(def); + * + * Iterator succs = ig.succs(defNode).iterator(); + * + * + * if (defNode.wide) { // Wide variables need two colors defNode.color = + * colorsUsed; + * + * if (CodeGenerator.DEBUG) { System.out.println(" assigning color " + + * colorsUsed + " to " + defNode); } colorsUsed=colorsUsed+2; ; } else { + * defNode.color = colorsUsed; + * + * if (CodeGenerator.DEBUG) { System.out.println(" assigning color " + + * colorsUsed + " to " + defNode); } colorsUsed++; } } + * + */ + + // END MOD + // Sort the uncolored nodes, by decreasing weight. Wide nodes + // have half their original weight since they take up two indices + // and we want to put color nodes with the lower indices. + Collections.sort(uncoloredNodes, new Comparator() { + public int compare(Object a, Object b) { + IGNode na = (IGNode) a; + IGNode nb = (IGNode) b; + + float wa = na.weight / ig.succs(na).size(); + float wb = nb.weight / ig.succs(nb).size(); + + if (na.wide) { + wa /= 2; + } + + if (nb.wide) { + wb /= 2; + } + + if (wb > wa) { + return 1; + } + + if (wb < wa) { + return -1; + } + + return 0; + } + }); + + nodes = uncoloredNodes.iterator(); + + while (nodes.hasNext()) { + IGNode node = (IGNode) nodes.next(); + + if (CodeGenerator.DEBUG) { + System.out.println("coloring " + node); + System.out.println(" conflicts " + ig.succs(node)); + } + + // Make sure node has not been colored + Assert.isTrue(node.color == -1); + + // Determine which colors have been assigned to the nodes + // conflicting with the node of interest + BitSet used = new BitSet(); + + Iterator succs = ig.succs(node).iterator(); + + while (succs.hasNext()) { + IGNode succ = (IGNode) succs.next(); + + if (succ.color != -1) { + used.set(succ.color); + + if (succ.wide) { + used.set(succ.color + 1); + } + } + } + + // Find the next available color + for (int i = 0; node.color == -1; i++) { + if (!used.get(i)) { + if (node.wide) { + // Wide variables need two colors + if (!used.get(i + 1)) { + node.color = i; + + if (CodeGenerator.DEBUG) { + System.out.println(" assigning color " + i + + " to " + node); + } + + if (i + 1 >= colorsUsed) { + colorsUsed = i + 2; + } + } + + } else { + node.color = i; + + if (CodeGenerator.DEBUG) { + System.out.println(" assigning color " + i + + " to " + node); + } + + if (i >= colorsUsed) { + colorsUsed = i + 1; + } + } + } + } + } + + nodes = ig.nodes().iterator(); + + while (nodes.hasNext()) { + IGNode node = (IGNode) nodes.next(); + + // Make sure each node has been colored + Assert.isTrue(node.color != -1, "No color for " + node); + + iter = node.defs.iterator(); + + // Set the index of the variable and all of its uses to be the + // chosen color. + while (iter.hasNext()) { + LocalExpr def = (LocalExpr) iter.next(); + def.setIndex(node.color); + + Iterator uses = def.uses().iterator(); + + while (uses.hasNext()) { + LocalExpr use = (LocalExpr) uses.next(); + use.setIndex(node.color); + } + } + } + + if (CodeGenerator.DEBUG) { + System.out.println("After allocating locals--------------------"); + cfg.print(System.out); + System.out.println("End print----------------------------------"); + } + } + + /** + * Returns the maximum number of local variables used by the cfg after its + * "registers" (local variables) have been allocated. + */ + public int maxLocals() { + return colorsUsed; + } + + /** + * Creates a new local variable in this method (as modeled by the cfg). + * Updates the number of local variables appropriately. + */ + public LocalVariableGen newLocal(Type type) { + // Why don't we add Type information to the LocalVariable? Are we + // assuming that type checking has already been done and so its a + // moot point? + + LocalVariableGen var = new LocalVariableGen(colorsUsed, "retAdr" + + colorsUsed, type, null, null); + colorsUsed += type.getSize(); + return var; + } + + /** + * IGNode is a node in the interference graph. Note that this node is + * different from the one in Liveness. For instance, this one stores + * information about a node's color, its weight, etc. Because nodes may be + * coalesced, an IGNode may represent more than one LocalExpr. That's why + * there is a list of definitions. + */ + class IGNode extends GraphNode { + Set defs; + + LocalExpr key; + + int color; + + boolean wide; // Is the variable wide? + + float weight; + + public IGNode(LocalExpr def) { + color = -1; + key = def; + defs = new LinkedHashSet(); + defs.add(def); + wide = (def.type().getSize() == 2); + computeWeight(); + } + + /** + * Coalesce two nodes in the interference graph. The weight of the other + * node is added to that of this node. This node also inherits all of + * the definitions of the other node. + */ + void coalesce(IGNode node) { + Assert.isTrue(wide == node.wide); + + weight += node.weight; + + Iterator iter = node.defs.iterator(); + + while (iter.hasNext()) { + LocalExpr def = (LocalExpr) iter.next(); + defs.add(def); + } + } + + public String toString() { + return "(color=" + color + " weight=" + weight + " " + + defs.toString() + ")"; + } + + /** + * Calculates the weight of a Block based on its loop depth. If the + * block does not exceed the MAX_DEPTH, then the weight is LOOP_FACTOR + * raised to the depth. + */ + private float blockWeight(Block block) { + int depth = cfg.loopDepth(block); + + if (depth > MAX_DEPTH) { + return MAX_WEIGHT; + } + + float w = 1.0F; + + while (depth-- > 0) { + w *= LOOP_FACTOR; + } + + return w; + } + + /** + * Computes the weight of a node in the interference graph. The weight + * is based on where the variable represented by this node is used. The + * method blockWeight is used to determine the weight of a variable used + * in a block based on the loop depth of the block. Special care must be + * taken if the variable is used in a PhiStmt. + */ + private void computeWeight() { + weight = 0.0F; + + Iterator iter = defs.iterator(); + + // Look at all(?) of the definitions of the IGNode + while (iter.hasNext()) { + LocalExpr def = (LocalExpr) iter.next(); + + weight += blockWeight(def.block()); + + Iterator uses = def.uses().iterator(); + + // If the variable is used as an operand to a PhiJoinStmt, + // find the predacessor block to the PhiJoinStmt in which the + // variable occurs and add the weight of that block to the + // running total weight. + while (uses.hasNext()) { + LocalExpr use = (LocalExpr) uses.next(); + + if (use.parent() instanceof PhiJoinStmt) { + PhiJoinStmt phi = (PhiJoinStmt) use.parent(); + + Iterator preds = cfg.preds(phi.block()).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + Expr op = phi.operandAt(pred); + + if (use == op) { + weight += blockWeight(pred); + break; + } + } + + } else if (use.parent() instanceof PhiCatchStmt) { + // If the variable is used in a PhiCatchStmt, add the + // weight of the block in which the variable is defined + // to + // the running total. + weight += blockWeight(use.def().block()); + + } else { + // Just add in the weight of the block in which the + // variable is used. + weight += blockWeight(use.block()); + } + } + } + } + } +} diff --git a/src/edu/purdue/cs/bloat/codegen/package.html b/src/edu/purdue/cs/bloat/codegen/package.html new file mode 100644 index 0000000..7a8b6d7 --- /dev/null +++ b/src/edu/purdue/cs/bloat/codegen/package.html @@ -0,0 +1,9 @@ + + + +

Generates Java bytecodes from the contents of a control flow graph. +It also performs "register" allocation of the local variables inside +the virtual machine.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/context/BloatContext.java b/src/edu/purdue/cs/bloat/context/BloatContext.java new file mode 100644 index 0000000..8f458e0 --- /dev/null +++ b/src/edu/purdue/cs/bloat/context/BloatContext.java @@ -0,0 +1,295 @@ +/* + * Class: BloatContext + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.context; + +import org.apache.bcel.generic.*; +import org.apache.bcel.util.Repository; +import org.apache.bcel.verifier.structurals.UninitializedObjectType; + +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.inline.CallGraph; +import edu.purdue.cs.bloat.inline.InlineContext; +import edu.purdue.cs.bloat.inline.InlineStats; + +import java.io.*; +import java.util.*; + +/** + * This abstract class is a central repository for all things that are necessary + * for a BLOATing sessions. Its subclasses implement certain schemes for + * managing BLOAT data structures such as editors and control flow graphs. + */ +public abstract class BloatContext implements InlineContext { + public static boolean DEBUG = Boolean.getBoolean("BloatContext.DEBUG"); + + protected InlineStats inlineStats; + + // Ignore stuff for inlining + protected Set ignorePackages = new LinkedHashSet(); + + protected Set ignoreClasses = new LinkedHashSet(); + + protected Set ignoreMethods = new LinkedHashSet(); + + protected Set ignoreFields = new LinkedHashSet(); + + protected boolean ignoreSystem = false; + + protected CallGraph callGraph; + + protected Set roots; // Root methods of call graph + + protected static void db(String s) { + if (DEBUG) + System.out.println(s); + } + + protected Repository loader; + + /** + * Constructor. Each BloatContext needs to know about a + * ClassInfoLoader. + */ + public BloatContext(Repository loader) { + this.loader = loader; + } + + private static ClassLoader systemCL; + static { + String s = ""; + systemCL = s.getClass().getClassLoader(); + } + + /** + * Returns true if the give type is a system class (that is, has + * the same class loader as java.lang.String). + */ + public static boolean isSystem(ObjectType type) { + Class c = null; + try { + c = Class.forName(type.getClassName().replace('/', '.')); + + } catch (ClassNotFoundException ex) { + System.err + .println("** Could not find class " + type.getClassName()); + ex.printStackTrace(System.err); + System.exit(1); + } + + // Have to use == because class loader might be null + return (c.getClassLoader() == systemCL); + } + + public void setRootMethods(Set roots) { + if (this.callGraph != null) { + // Can't set the call graph roots after its been created + throw new IllegalStateException("Cannot set roots after " + + "call graph has been created"); + } + + this.roots = roots; + } + + public CallGraph getCallGraph() { + if (this.callGraph == null) { + // Create a new CallGraph + this.callGraph = new CallGraph(this, this.roots); + } + return (this.callGraph); + } + + public InlineStats getInlineStats() { + if (inlineStats == null) { + inlineStats = new InlineStats(); + } + return (inlineStats); + } + + public void addIgnorePackage(String name) { + name = name.replace('.', '/'); + ignorePackages.add(name); + } + + public void addIgnoreClass(Type type) { + ignoreClasses.add(type); + } + + public void addIgnoreMethod(MethodRef method) { + ignoreMethods.add(method); + } + + public void addIgnoreField(MemberRef field) { + ignoreFields.add(field); + } + + public void setIgnoreSystem(boolean ignore) { + this.ignoreSystem = ignore; + } + + public boolean ignoreClass(Type type) { + // First, check to see if we explicitly ignore it. If not, check + // to see if we ignore its package. The ladies always seem to + // ignore my package. + if (type instanceof ReferenceType) + if (type instanceof ArrayType) + type = ((ArrayType) type).getBasicType(); + else if (type instanceof UninitializedObjectType) + type = ((UninitializedObjectType) type).getInitialized(); + // CONVERT ARRAYTYPES ETC TO OBJECTTYPES OR BASICTYPES + + if (ignoreClasses.contains(type)) { + return (true); + + } else if (type instanceof BasicType) { + addIgnoreClass(type); + return (true); + + } else { + ObjectType objType = (ObjectType) type; + if (this.ignoreSystem) { + if (isSystem(objType)) { + addIgnoreClass(objType); + return (true); + } + } + + String packageName = objType.getClassName(); + int lastSlash = packageName.lastIndexOf('/'); + + if (lastSlash == -1) { + return (false); + } + + packageName = packageName.substring(0, lastSlash); + + // If any ignore package is a prefix of the class's package, + // then ignore it. This makes our lives easier. + Iterator packages = ignorePackages.iterator(); + while (packages.hasNext()) { + String s = (String) packages.next(); + if (objType.getClassName().startsWith(s)) { + addIgnoreClass(type); + return (true); + } + } + + return (false); + } + } + + public boolean ignoreMethod(MethodRef method) { + if (ignoreMethods.contains(method)) { + return (true); + + } else if (ignoreClass(method.declaringClass())) { + addIgnoreMethod(method); + return (true); + } + return (false); + } + + public boolean ignoreField(MemberRef field) { + if (ignoreMethods.contains(field)) { + return (true); + + } else if (ignoreClass(field.declaringClass())) { + addIgnoreField(field); + return (true); + } + return (false); + } + + /** + * Commits all classes, methods, and fields, that have been modified. + */ + public abstract void commitDirty(); + + /** + * Test the ignore stuff. + */ + public static void main(String[] args) { + PrintWriter out = new PrintWriter(System.out, true); + PrintWriter err = new PrintWriter(System.err, true); + + BloatContext context = new PersistentBloatContext( + org.apache.bcel.Repository.getRepository()); + // new CachingBloatContext(new ClassFileLoader(), new ArrayList(), + // false); + + List types = new ArrayList(); + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-ip")) { + if (++i >= args.length) { + err.println("** Missing package name"); + System.exit(1); + } + + out.println("Ignoring package " + args[i]); + context.addIgnorePackage(args[i]); + + } else if (args[i].equals("-ic")) { + if (++i >= args.length) { + err.println("** Missing class name"); + System.exit(1); + } + + out.println("Ignoring class " + args[i]); + String type = args[i].replace('.', '/'); + context.addIgnoreClass(Type.getType("L" + type + ";")); + + } else { + // A type + String type = args[i].replace('.', '/'); + types.add(Type.getType("L" + type + ";")); + } + } + + out.println(""); + + Iterator iter = types.iterator(); + while (iter.hasNext()) { + Type type = (Type) iter.next(); + out.println("Ignore " + type + "? " + context.ignoreClass(type)); + } + } + +} diff --git a/src/edu/purdue/cs/bloat/context/Makefile b/src/edu/purdue/cs/bloat/context/Makefile new file mode 100644 index 0000000..1265afe --- /dev/null +++ b/src/edu/purdue/cs/bloat/context/Makefile @@ -0,0 +1,26 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + BloatContext.class\ + PersistentBloatContext.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/context/PersistentBloatContext.java b/src/edu/purdue/cs/bloat/context/PersistentBloatContext.java new file mode 100644 index 0000000..efb6623 --- /dev/null +++ b/src/edu/purdue/cs/bloat/context/PersistentBloatContext.java @@ -0,0 +1,483 @@ +/* + * Class: PersistentBloatContext + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.context; + +import org.apache.bcel.verifier.structurals.UninitializedObjectType; +import org.apache.bcel.util.Repository; +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +import edu.purdue.cs.bloat.editor.ClassHierarchy; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.editor.NameAndType; + +import java.util.*; + +/** + * Maintains all BLOAT data structures as if they were meant to reside in a + * persistent store. As a result, it keeps every piece of BLOAT data around + * because it might be needed in the future. No fancing cache maintainence is + * performed. Because we are going for maximum information we take the closure + * of classes when working with the class hierarchy. + */ +public class PersistentBloatContext extends BloatContext { + + protected final ClassHierarchy hierarchy; + + protected Map classEditors; // Maps ClassInfos to ClassEditors + + protected Map classInfos; // Maps ClassInfos to ClassEditors + + public static boolean DB_COMMIT = true; + + protected static void comm(String s) { + if (DB_COMMIT || DEBUG) { + System.out.println(s); + } + } + + /** + * Constructor. Each BloatContext stems from a + * ClassInfoLoader. Using the loader it can create an + * Editor and such. Initially, no classes are loaded. + */ + public PersistentBloatContext(Repository loader) { + this(loader, true); + } + + /** + * Constructor. It is the responsibility of the subclasses to add classes to + * the hierarchy by calling addClasses. + * + * @param loader + * Used to load classes + * @param closure + * Do we look for the maximum number of classes? + */ + public PersistentBloatContext(Repository loader, boolean closure) { + super(loader); + db("Creating a new BloatContext"); + + // Create a bunch of the mappings we maintain. Make sure to do + // this before anything else! + + classEditors = new LinkedHashMap(); + classInfos = new LinkedHashMap(); + + // Have to create an empty class hierarchy then add the classes. + // There is a strange circular dependence between the hierarchy + // and the context. + this.hierarchy = new ClassHierarchy(this, new ArrayList(), closure); + } + + /** + * Adds a bunch of (names of) classes to the hierarchy. + */ + protected void addClasses(Collection classes) { + Iterator iter = classes.iterator(); + while (iter.hasNext()) { + String className = (String) iter.next(); + this.hierarchy.addClassNamed(className); + } + } + + public JavaClass loadClass(String className) throws ClassNotFoundException { + // Lots of interesting stuff to do here. For the moment, just + // load the class from the ClassInfoLoader and add it to the + // hierarchy. + + className = className.replace('.', '/'); + + // Check the cache of ClassInfos + JavaClass info = (JavaClass) classInfos.get(className); + + if (info == null) { + db("BloatContext: Loading class " + className); + info = loader.loadClass(className); + hierarchy.addClassNamed(className); + db("loadClass: " + className + " -> " + info); + classInfos.put(className, info); + } + + return (info); + } + + public JavaClass newClassInfo(int class_name_index, + int superclass_name_index, String file_name, int major, int minor, + int access_flags, ConstantPool constant_pool, int[] interfaces, + Field[] fields, Method[] methods, Attribute[] attributes) { + JavaClass jc = new JavaClass(class_name_index, superclass_name_index, + file_name, major, minor, access_flags, constant_pool, + interfaces, fields, methods, attributes); + String className = jc.getClassName(); + + db("newClassEditor(...........): " + className + " -> " + jc); + + classInfos.put(className, jc); + return jc; + } + + public ClassGen newClass(int accessflags, String className, + String file_name, Type superType, Type[] interfaces) { + String[] interNames = new String[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interNames[i] = ((ObjectType) interfaces[i]).getClassName(); + } + + ClassGen cg = new ClassGen(className, ((ObjectType) superType) + .getClassName(), file_name, accessflags, interNames); + JavaClass jc = cg.getJavaClass(); + String classname = cg.getClassName(); + db("newClassInfo(.....): " + classname + " -> " + cg); + classEditors.put(classname, cg); + classInfos.put(classname, jc); + return cg; + } + + /* + * public ClassInfo newClassInfo(int modifiers, int classIndex, int + * superClassIndex, int[] interfaceIndexes, List constants) { + * + * return this.loader.storeClass(modifiers, classIndex, superClassIndex, + * interfaceIndexes, constants); } + */ + + public ClassHierarchy getHierarchy() { + return (this.hierarchy); + } + + public ClassGen newClass(int class_name_index, int superclass_name_index, + String file_name, int major, int minor, int access_flags, + ConstantPool constant_pool, int[] interfaces, Field[] fields, + Method[] methods, Attribute[] attributes) { + JavaClass jc = new JavaClass(class_name_index, superclass_name_index, + file_name, major, minor, access_flags, constant_pool, + interfaces, fields, methods, attributes); + + ClassGen ce = new ClassGen(jc); + + String className = ce.getClassName(); + + db("editClass(ClassInfo): " + className + " -> " + ce); + + classEditors.put(className, ce); + classInfos.put(className, jc); + return ce; + } + + public ClassGen editClass(String className) throws ClassNotFoundException, + ClassFormatException { + // Only make the name -> classInfo mapping if we edit the class, + // this way the mapping will be deleted when the ClassEditor is + // released. + + // className = className.intern(); + + ClassGen info = (ClassGen) classEditors.get(className); + + if (info == null) { + JavaClass jc = loadClass(className); + db("editClass(String): " + className + " -> " + info); + return editClass(jc); + } + + return (info); + } + + public ClassGen editClass(Type classType) throws ClassNotFoundException, + ClassFormatException { + if (classType instanceof ReferenceType) { + if (classType instanceof ObjectType) + return editClass((ObjectType) classType); + else if (classType instanceof ArrayType) + return editClass(((ArrayType) classType).getBasicType()); + else if (classType instanceof UninitializedObjectType) + return editClass(((UninitializedObjectType) classType) + .getInitialized()); + } else + throw new ClassNotFoundException(classType + + "is not an ObjectType and cannot be loaded"); + return null; + } + + public ClassGen editClass(ObjectType classType) + throws ClassNotFoundException, ClassFormatException { + return (editClass(classType.getClassName())); + } + + public ClassGen editClass(JavaClass info) { + // Check the cache + ClassGen ce = (ClassGen) classEditors.get(info.getClassName()); + + if (ce == null) { + ce = new ClassGen(info); + classInfos.put(info.getClassName(), info); + classEditors.put(info.getClassName(), ce); + + // if(!classInfos.containsValue(info)) { + // String className = ce.getClassName().intern(); + // db("editClass(ClassInfo): " + className + " -> " + info); + // classInfos.put(className, info); + // } + } + + return (ce); + } + + public MethodGen editMethod(MethodRef method) throws NoSuchMethodException { + + // Groan, we have to do this the HARD way. + db("Creating a new MethodEditor for " + method); + String name = method.name(); + String signature = method.getSignature(); + + try { + ClassGen ce = editClass(method.declaringClass()); + Method[] methods = ce.getMethods(); + + for (int i = 0; i < methods.length; i++) { + MethodGen me = new MethodGen(methods[i], method + .declaringClass().getSignature(), ce.getConstantPool());// TODO + + if (me.getName().equals(name) + && me.getSignature().equals(signature)) { + // The call to editMethod should have already handled + // the methodEditors mapping, but we still need to do + // methodInfos. + return (me); + } + + // release(methods[i], ce.getClassName()); + } + + } catch (ClassNotFoundException ex1) { + } catch (ClassFormatException ex2) { + } + + throw new NoSuchMethodException(method.toString()); + } + + public MethodGen editMethod(Method info, String declaringClass) { + // Groan, we have to do this the HARD way. + db("Creating a new MethodEditor for " + info.getName()); + String name = info.getName(); + String signature = info.getSignature(); + + try { + ClassGen ce = editClass(declaringClass); + Method[] methods = ce.getMethods(); + + for (int i = 0; i < methods.length; i++) { + + if (methods[i].getName().equals(name) + && methods[i].getSignature().equals(signature)) { + MethodGen me = new MethodGen(methods[i], declaringClass, ce + .getConstantPool()); + // The call to editMethod should have already handled + // the methodEditors mapping, but we still need to do + // methodInfos. + return (me); + } + + // release(methods[i], ce.getClassName()); + } + + } catch (ClassNotFoundException ex1) { + } catch (ClassFormatException ex2) { + } + return null; + // throw new NoSuchMethodException(info.toString()); + } + + public FieldGen editField(MemberRef field) throws NoSuchFieldException { + + // Just like we had to do with methods + + NameAndType nat = field.nameAndType(); + String name = nat.name(); + Type type = nat.type(); + + try { + ClassGen ce = editClass(field.declaringClass()); + Field[] fields = ce.getFields(); + + for (int i = 0; i < fields.length; i++) { + FieldGen fe = editField(fields[i], field.declaringClass() + .getSignature());// TODO + + if (fe.getName().equals(name) && fe.getType().equals(type)) { + return (fe); + } + + } + } catch (ClassNotFoundException ex1) { + } catch (ClassFormatException ex2) { + } + return null; + // throw new NoSuchFieldException(field.toString()); + } + + public FieldGen editField(Field info, String className) { + // Check the cache + String name = info.getName(); + Type type = info.getType(); + + try { + ClassGen ce = editClass(className); + Field[] fields = ce.getFields(); + + for (int i = 0; i < fields.length; i++) { + FieldGen fe = editField(fields[i], className); + + if (fe.getName().equals(name) && fe.getType().equals(type)) { + return (fe); + } + + } + } catch (ClassNotFoundException ex1) { + } catch (ClassFormatException ex2) { + } + return null; + // throw new NoSuchFieldException(info.toString()); + + } + + public void release(JavaClass info) { + // Since we keep around all data, do nothing + } + + public void release(ClassGen ce) { + // Since we keep around all data, do nothing + } + + public void release(Method info, String className) { + // Since we keep around all data, do nothing + } + + public void release(Field info, String className) { + // Since we keep around all data, do nothing + } + + /** + * Classes that are ignored are not committed. + * + * @see ignoreClass + */ + public void commit(JavaClass info) { + ClassGen cg = (ClassGen) classEditors.get(info.getClassName()); + JavaClass jc = (JavaClass) cg.getJavaClass(); + classInfos.put(info.getClassName(), jc); + } + + public void commit(ClassGen info) { + classEditors.put(info.getClassName(), info); + } + + public void commit(MethodGen methodGen) { + this.commit(methodGen.getMethod(), methodGen.getClassName()); + } + + public void commit(Method m, String classname) { + try { + ClassGen cg = editClass(classname); + Method oldMethod = cg.containsMethod(m.getName(), m.getSignature()); + cg.replaceMethod(oldMethod, m); + } catch (ClassNotFoundException cnfe) { + // IF THERE IS NO CLASS THERE IS NOTHING TO COMMIT, SHOULDN'T + // HAPPEN + } + } + + /* + * private Method commit(Method m){ try{ ClassGen cg = editClass(classname); + * Method oldMethod = cg.containsMethod(m.getName(),m.getSignature()); + * cg.replaceMethod(oldMethod, m); }catch(ClassNotFoundException cnfe){ //IF + * THERE IS NO CLASS THERE IS NOTHING TO COMMIT, SHOULDN'T HAPPEN } } + */ + public void commit(Field f, String classname) { + try { + ClassGen cg = editClass(classname); // Ask to commit the ClassGen + Field oldField = cg.containsField(f.getName()); + cg.replaceField(oldField, f); + } catch (ClassNotFoundException cnfe) { + // IF THERE IS NO CLASS THERE IS NOTHING TO COMMIT, SHOULDN'T + // HAPPEN + } + } + + /* + * private Field commit(Field f){ FieldGen fg = + * (FieldGen)fieldEditors.get(f); if(fg != null){ fg.update(); Field + * newField = fg.getField(); //return final version of fg + * fieldEditors.remove(f); fieldEditors.put(newField, fg); return newField; + * }else{ return f; //return the old field } } + */ + + // THE COMMIT OF A METHOD/FIELD SHOULD RESULT IN THE COMMIT OF THE + // PARENT + // CLASS SO commit(JavaClass) SHOULD SUFFICE. + // Reason I got rid of commit(method) is bcel's Method has no mapping to + // the class that the method belongs to. Each editField or editMethod + // ensures that the class is loaded. (release does nothing) + public void commit() { + } + + public void commitDirty() { + } + + /* + * // Commit all dirty fields Object[] array = + * this.fieldEditors.values().toArray(); for(int i = 0; i < array.length; + * i++) { FieldEditor fe = (FieldEditor) array[i]; if(fe.isDirty() && + * !ignoreField(fe.memberRef())) { comm(" Committing field: " + + * fe.declaringClass().name() + "." + fe.name()); commit(fe.fieldInfo()); } } // + * Commit all dirty methods array = this.methodEditors.values().toArray(); + * for(int i = 0; i < array.length; i++) { MethodEditor me = (MethodEditor) + * array[i]; if(me.isDirty() && !ignoreMethod(me.memberRef())) { comm(" + * Committing method: " + me.declaringClass().name() + "." + me.name() + + * me.type()); commit(me.methodInfo()); } } // Commit all dirty classes + * array = this.classEditors.values().toArray(); for(int i = 0; i < + * array.length; i++) { ClassEditor ce = (ClassEditor) array[i]; + * if(ce.isDirty() && !ignoreClass(ce.type())) { comm(" Committing class: " + + * ce.name()); commit(ce.classInfo()); } } } + */ + +} diff --git a/src/edu/purdue/cs/bloat/context/package.html b/src/edu/purdue/cs/bloat/context/package.html new file mode 100644 index 0000000..65f4f14 --- /dev/null +++ b/src/edu/purdue/cs/bloat/context/package.html @@ -0,0 +1,17 @@ + + + +

Repositories for important BLOAT objects such as the +Editor, ClassHierarchy, and FlowGraphs. In +order to avoid build dependencies with other packages we took the +novel approach of having a "context" interface in several packages that +the classes in this package then implement.

+ +

The reason we implement all of the methods in one class is so that +if the user wants to have different contexts with different attributes +(for instance, caching policies), all one has to do is subclass +BloatContext instead of subclassing other contextes which may +become ackward.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/diva/InductionVarAnalyzer.java b/src/edu/purdue/cs/bloat/diva/InductionVarAnalyzer.java new file mode 100644 index 0000000..99b37a5 --- /dev/null +++ b/src/edu/purdue/cs/bloat/diva/InductionVarAnalyzer.java @@ -0,0 +1,537 @@ +/* + * Class: InductionVarAnalyzer + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +/* Demand-driven Induction Variable Analysis (diva)*/ +package edu.purdue.cs.bloat.diva; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.ssa.ComponentVisitor; +import edu.purdue.cs.bloat.ssa.SSAGraph; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.IfCmpStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MemExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.SCStmt; +import edu.purdue.cs.bloat.tree.SRStmt; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.Swizzler; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; + +/** + * InductionVarAnalyzer traverses a control flow graph and looks for array + * element swizzle operations inside loops. If possible, these swizzle + * operations are hoisted out of the loop and are replaced with range swizzle + * operations. The technique used is Demand-driven Induction Variable Analysis + * (DIVA). + *

+ * To accomplish its tasks, InductionVarAnalyzer keeps track of a number of + * induction variables (represented by Swizzler objects) and local variables. + * + * @see Swizzler + * @see LocalExpr + */ +public class InductionVarAnalyzer implements Optimization { + public static boolean DEBUG = false; + + SSAGraph ssaGraph; + + FlowGraph CFG; // Control flow graph being operated on + + LinkedHashMap IndStore; // Stores induction variables and + + // associated swizzlers + LinkedHashMap LocalStore; // Stores local variables + + Expr ind_var = null; // An induction variable + + Expr ind_init = null; // Initial value of an induction variable + + Expr ind_term = null; // Not used??? + + Expr ind_inc = null; // Expression used to increment induction + + // variable (all uses commented out) + Expr tgt = null; // Target of the phi statement that defines + + // an induction var + + boolean changed = false; // Was the cfg changed? + + /** + * Searches the list of induction variables for an induction variable with a + * given value number. + * + * @param vn + * Value number to search for. + * + * @return Swizzler object whose target has the desired value number or + * null, if value number is not found. + */ + public Swizzler getSwizzler(int vn) { + Iterator iter = IndStore.values().iterator(); + + while (iter.hasNext()) { + Swizzler swz = (Swizzler) iter.next(); + if ((swz.target().valueNumber() == vn) + || (swz.ind_var().valueNumber() == vn)) + return swz; + } + return null; + } + + /** + * Searchs the stored list of local variables for a local variable with a + * given value number. + * + * @param vn + * The value number to search for. + * + * @return The local variable with the given value number, or null, if not + * found. + */ + public MemExpr getLocal(int vn) { + Iterator iter = LocalStore.keySet().iterator(); + + while (iter.hasNext()) { + MemExpr le = (MemExpr) iter.next(); + if (le.valueNumber() == vn) + return le; + } + return null; + } + + /** + * Displays (to System.out) all of the Swizzlers stored in the induction + * variable store. + * + * @see Swizzler + */ + public void Display_store() { + Iterator iter = IndStore.values().iterator(); + + while (iter.hasNext()) { + Swizzler indswz = (Swizzler) iter.next(); + + System.out.println("\nIV: " + indswz.ind_var() + " tgt: " + + indswz.target() + "\narray: " + indswz.array() + + " init: " + indswz.init_val() + " end: " + + indswz.end_val()); + } + } + + /** + * Displays the contents of a Swizzler object to System.out. + * + * @param indswz + * The Swizzler to display. + */ + public void displaySwizzler(Swizzler indswz) { + System.out.println("\nIV: " + indswz.ind_var() + "vn:" + + indswz.ind_var().valueNumber() + " tgt: " + indswz.target() + + "vn:" + indswz.target().valueNumber() + "\narray: " + + indswz.array() + " init: " + indswz.init_val() + " end: " + + indswz.end_val()); + } + + /** + * Adds a swizzle range statement (SRStmt) to the end of each predecessor + * block of the block containing the phi statement that defines an induction + * variable provided that the phi block does not dominate its predacessor. + * Phew. + * + * @param indswz + * Swizzler representing the induction variable on which the + * range swizzle statement is added. + */ + public void insert_aswrange(Swizzler indswz) { + Iterator iter = CFG.preds(indswz.phi_block()).iterator(); + while (iter.hasNext()) { + Block blk = (Block) iter.next(); + if (!indswz.phi_block().dominates(blk)) { + SRStmt aswrange = new SRStmt((Expr) indswz.array().clone(), + (Expr) indswz.init_val().clone(), (Expr) indswz + .end_val().clone()); + blk.tree().addStmtBeforeJump(aswrange); + changed = true; + if (DEBUG) { + System.out.println("Inserted ASWR: " + aswrange + + "\nin block: " + blk); + + System.out.println("$$$ can insert aswrange now\n" + + "array: " + indswz.array() + "\nIV: " + + indswz.ind_var() + "\ninit: " + indswz.init_val() + + "\nend: " + indswz.end_val()); + } + } + } + } + + /* To determine if a phi statement is a mu */ + /* Returns null if not a MU and sets ind_var & ind_init */ + /* to refer to the IV & its initial value otherwise */ + + /** + * Determines whether or not a phi statement is a mu function. A mu function + * is a phi function that merges values at loop headers, as opposed to those + * that occur as a result of forward branching. Mu functions always have two + * arguments. + * + * @param phi + * phi statement that may be a mu function + * @param cfg + * CFG to look through Get rid of this + * parameter + * + * @return The block containing the mu functions external (that is, outside + * the loop) argument, also known as the external ssalink. If the + * phi statement is not a mu function, null is returned. + */ + public Block isMu(PhiJoinStmt phi, FlowGraph cfg) { + // Does it contain two operands? + if (phi.numOperands() != 2) + return null; + + // Is it REDUCIBLE? + if (cfg.blockType(phi.block()) == Block.IRREDUCIBLE + || cfg.blockType(phi.block()) == Block.NON_HEADER) { + return null; + } + + /* + * Does one of them dominate the phi and the other dominated by the phi? + */ + + Iterator iter = cfg.preds(phi.block()).iterator(); + Block pred1 = (Block) iter.next(); + Block pred2 = (Block) iter.next(); + + if (pred1.dominates(phi.block()) && phi.block().dominates(pred2) + && (pred1 != phi.block())) { + if (DEBUG) + System.out.println("Extlink = 1 pred1:" + pred1 + " pred2:" + + pred2); + ind_var = phi.operandAt(pred2); + ind_init = phi.operandAt(pred1); + return pred1; + } + if (pred2.dominates(phi.block()) && phi.block().dominates(pred1) + && (pred2 != phi.block())) { + if (DEBUG) + System.out.println("Extlink = 2 pred1:" + pred1 + " pred2:" + + pred2); + ind_var = phi.operandAt(pred1); + ind_init = phi.operandAt(pred2); + return pred2; + } + + return null; + + } + + /** + * Performs DIVA on a CFG public static void transform(FlowGraph cfg) { // + * Create a new instance to allow multiple threads. InductionVarAnalyzer me = + * new InductionVarAnalyzer(); me.transform(cfg); } + */ + + public void transform(MethodState state) { + transform(state.controlFlowGraph()); + } + + /** + * Performs DIVA on a CFG. + */ + private void transform(FlowGraph cfg) { + ssaGraph = new SSAGraph(cfg); + CFG = cfg; + IndStore = new LinkedHashMap(); + LocalStore = new LinkedHashMap(); + changed = false; + + if (DEBUG) { + System.out + .println("----------Before visitComponents--------------"); + cfg.print(System.out); + } + + // Visit each strongly connected component (SCC) in the CFG. SCCs + // correspond to sequences in the program. Visit each node in the + // SCC and build the local variable store. Create Swizzlers at + // PhiStmts, if approproate, and store them in the induction + // variable store. If it can be determined that an array element + // swizzle can be hoisted out of a loop, it is hoisted. + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(List scc) { + if (DEBUG) + System.out.println("SCC ="); + + Iterator e = scc.iterator(); + + while (e.hasNext()) { + Node v = (Node) e.next(); + if (DEBUG) + System.out.println(" " + v + "{" + v.key() + "} " + + v.getClass()); + + v.visit(new TreeVisitor() { + public void visitPhiJoinStmt(PhiJoinStmt phi) { + if (isMu(phi, CFG) != null) { + // Iterator iter = phi.operands().iterator(); + tgt = phi.target(); + if (DEBUG) + System.out.println("IV:" + ind_var + " VN:" + + ind_var.valueNumber() + "\ninit:" + + ind_init + " target: " + tgt + + " VN: " + tgt.valueNumber()); + Swizzler swz = new Swizzler(ind_var, tgt, + ind_init, phi.block()); + if (DEBUG) { + System.out + .println("store swizzler for " + + ind_var.def() + " & " + + tgt.def()); + displaySwizzler(swz); + } + + if (ind_var.def() != null) + IndStore.put(ind_var.def(), swz); + + if (tgt.def() != null) + IndStore.put(tgt.def(), swz); + + if (DEBUG) + System.out.println(" Mu: " + phi + "{" + + phi.key() + "}"); + } else { + if (DEBUG) + System.out.println("Phi: " + phi + "{" + + phi.key() + "}"); + } + } + + public void visitLocalExpr(LocalExpr me) { + if (me.def() != null) { + if (LocalStore.get(me.def()) == null) { + LocalStore.put(me.def(), me); + } + } + if (LocalStore.get(me) == null) { + LocalStore.put(me, me); + } + if (DEBUG) + System.out.println("stored ME: " + me + + " vn: " + me.valueNumber()); + } + + public void visitStaticFieldExpr(StaticFieldExpr me) { + if (me.def() != null) { + if (LocalStore.get(me.def()) == null) { + LocalStore.put(me.def(), me); + } + } + if (LocalStore.get(me) == null) { + LocalStore.put(me, me); + } + if (DEBUG) + System.out.println("stored ME: " + me + + " vn: " + me.valueNumber()); + } + + public void visitIfCmpStmt(IfCmpStmt cmp) { + Swizzler indswz = null; + boolean set_term = false; + + if (cmp.left().def() != null) + indswz = (Swizzler) IndStore.get(cmp.left() + .def()); + if (indswz != null) { + if (DEBUG) + displaySwizzler(indswz); + if (indswz.end_val() == null) { + indswz.set_end_val(cmp.right()); + set_term = true; + if (DEBUG) + System.out.println("Set end_val of " + + indswz.ind_var() + " to " + + cmp.right()); + } + } else { + if (cmp.right().def() != null) + indswz = (Swizzler) IndStore.get(cmp + .right().def()); + if (indswz != null) { + if (DEBUG) + displaySwizzler(indswz); + if (indswz.end_val() == null) { + indswz.set_end_val(cmp.left()); + set_term = true; + if (DEBUG) + System.out + .println("Set end_val of " + + indswz.ind_var() + + " to " + + cmp.left()); + } + } + } + + if (set_term && indswz != null + && indswz.array() != null) { + indswz.aswizzle().set_redundant(true); + insert_aswrange(indswz); + } + } + + public void visitSCStmt(SCStmt sc) { + Swizzler indswz; + MemExpr le = null; + + if (DEBUG) + System.out.println("SC: array= " + sc.array() + + " VN:" + sc.array().valueNumber() + + "\nindex=" + sc.index() + " VN:" + + sc.index().valueNumber()); + + indswz = getSwizzler(sc.index().valueNumber()); + if (indswz != null) { + if (DEBUG) + displaySwizzler(indswz); + if (indswz.array() == null) { + le = getLocal(sc.array().valueNumber()); + if (le == null && sc.array().def() != null) + le = getLocal(sc.array().def() + .valueNumber()); + if (le != null) { + if (DEBUG) + System.out.println("Le: " + le); + indswz.set_array(le); + indswz.set_aswizzle(sc); + } else + return; + } + if (indswz.end_val() != null) { + sc.set_redundant(true); + insert_aswrange(indswz); + } + } + } + + /* + * public void visitStoreExpr(StoreExpr ind_store) { + * if(ind_var != null) { + * if(ind_var.equalsExpr(ind_store.target())) { if (tgt != + * null && ind_store.expr() instanceof ArithExpr){ + * ArithExpr ind_exp = (ArithExpr)ind_store.expr(); + * if(tgt.equalsExpr(ind_exp.left())) ind_inc = + * ind_exp.right(); else if(tgt.equals(ind_exp.right())) + * ind_inc = ind_exp.left(); else { ind_inc = null; + * return; } System.out.println("Ind_inc: "+ind_inc); } } } } + */ + + }); + + } + + } + + }); + + if (DEBUG) { + System.out.println("------------After visitComponents---------"); + cfg.print(System.out); + } + + // If the CFG changed (i.e. if an array range swizzle was added), + // traverse + // the graph and remove redundent swizzle statements. + if (changed) { + cfg.visit(new TreeVisitor() { + ListIterator iter; + + public void visitTree(Tree tree) { + iter = tree.stmts().listIterator(); + + while (iter.hasNext()) { + Stmt stmt = (Stmt) iter.next(); + stmt.visit(this); + } + } + + public void visitSCStmt(SCStmt sc) { + Object dup2stmt; + if (sc.redundant()) { + + iter.remove(); + dup2stmt = iter.previous(); + iter.remove(); + if (DEBUG) + System.out.println("Removed Redundant ASW: " + sc + + "\nand " + dup2stmt); + } + } + }); + } + + if (DEBUG) { + System.out.println("----------------After cfg.visit--------------"); + cfg.print(System.out); + } + } + + public String traceMessage(String dateString) { + return " DIVA: " + dateString; + } + + public String preDebugMessage() { + return "-----Before DIVA------"; + } + + public String postDebugMessage() { + return "-----After DIVA-----"; + } +} diff --git a/src/edu/purdue/cs/bloat/diva/Makefile b/src/edu/purdue/cs/bloat/diva/Makefile new file mode 100644 index 0000000..fbfa315 --- /dev/null +++ b/src/edu/purdue/cs/bloat/diva/Makefile @@ -0,0 +1,25 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + InductionVarAnalyzer.class\ + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/diva/package.html b/src/edu/purdue/cs/bloat/diva/package.html new file mode 100644 index 0000000..f80f7e3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/diva/package.html @@ -0,0 +1,8 @@ + + + +

Performs demand-driven induction variable analysis on a Java +classfile.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/editor/ClassHierarchy.java b/src/edu/purdue/cs/bloat/editor/ClassHierarchy.java new file mode 100644 index 0000000..8bd606d --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/ClassHierarchy.java @@ -0,0 +1,1281 @@ +/* + * Class: ClassHierarchy + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +import edu.purdue.cs.bloat.tbaa.TypeInference; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.Graph; +import edu.purdue.cs.bloat.util.GraphNode; + +import java.util.*; +import java.io.*; + +/** + * ClassHierarchy maintains a graph of the subclass relationships of the classes + * loaded by the ClassInfoLoader. + * + * @see ClassInfoLoader + */ +public class ClassHierarchy { + public static final Type POS_SHORT = Type.getType("L+short!;"); + + public static final Type POS_BYTE = Type.getType("L+byte!;"); + + static final int MAX_INT = 8; + + static final int MAX_SHORT = 7; + + static final int MAX_CHAR = 6; + + static final int MAX_BYTE = 5; + + static final int MAX_BOOL = 4; + + static final int MIN_CHAR = 3; + + static final int MIN_BOOL = 3; + + static final int ZERO = 3; + + static final int MIN_BYTE = 2; + + static final int MIN_SHORT = 1; + + static final int MIN_INT = 0; + + public static boolean DEBUG = false; + + public static boolean RELAX = true; + + Set classes; // The Types of the classes in hierarchy + + Graph extendsGraph; // "Who extends who" graph + + Graph implementsGraph; // "Who implements what" graph + + boolean closure; // Do we visit all referenced classes? + + // Maps methods to the methods they may resolve to + private Map resolvesToCache; + + // These are only needed during construction. + EditorContext context; // MAYBE SHOULD BE CLASSLOADER? + + LinkedList worklist; + + Set inWorklist; + + private void db(String s) { + if (DEBUG) + System.out.println(s); + } + + /** + * Constructor. + * + * @param context + * The context in which to access an Editor and other + * such things. + * @param initial + * The names of the classes that initially constitue the + * hierarchy. + * @param closure + * Do we get the maximum amount of class information? + */ + public ClassHierarchy(EditorContext context, Collection initial, + boolean closure) { + this.context = context; + this.closure = closure; + + classes = new LinkedHashSet(); + extendsGraph = new Graph(); + implementsGraph = new Graph(); + + worklist = new LinkedList(); + inWorklist = new LinkedHashSet(); + + this.resolvesToCache = new LinkedHashMap(); + + // Need new ArrayList to avoid ConcurrentModificationException + Iterator iter = new ArrayList(initial).iterator(); + + while (iter.hasNext()) { + Type type = (Type) iter.next(); + if (type instanceof ObjectType) { + String name = ((ObjectType) type).getClassName(); + addClass(name); + } + } + } + + /** + * Adds a class of a given name to the ClassHierarchy. + */ + public void addClassNamed(String name) { + addClass(name); + } + + private void replaceTypeNodesWithTypes(List list) { + ListIterator iterator = list.listIterator(); + + while (iterator.hasNext()) { + TypeNode node = (TypeNode) iterator.next(); + iterator.set(node.type); + } + } + + /** + * Returns the immediate subclasses of a given Type as a + * Collection of Types. + * + *

+ * + * The subclass relationship at the classfile level is a little screwy with + * respect to interfaces. An interface that extends another interface is + * compiled into an interface that extends java.lang.Object and implements + * the superinterface. As a result, the interface-subinterface is not + * captured in subclasses as one may expect. Instead, you have to + * look at implementors and filter out the classes. + */ + public Collection subclasses(Type type) { + TypeNode node = (TypeNode) getExtendsNode(type); + + if (node != null) { + List list = new ArrayList(extendsGraph.preds(node)); + replaceTypeNodesWithTypes(list); + + return list; + } + + return new ArrayList(); + } + + /** + * Returns the superclass of a given Type. If the Type + * has no superclass (that is it is Type.OBJECT), then null is + * returned. + */ + public Type superclass(Type type) { + TypeNode node = (TypeNode) getExtendsNode(type); + + if (node != null) { + Collection succs = extendsGraph.succs(node); + + Iterator iter = succs.iterator(); + + if (iter.hasNext()) { + TypeNode v = (TypeNode) iter.next(); + return v.type; + } + } + + return null; + } + + /** + * Returns the interfaces that a given Type implements as a + * Collection of Types + */ + public Collection interfaces(Type type) { + TypeNode node = (TypeNode) getImplementsNode(type); + + if (node != null) { + List list = new ArrayList(implementsGraph.succs(node)); + replaceTypeNodesWithTypes(list); + return list; + } + + return new ArrayList(); + } + + /** + * Returns the classes (Types) that implement a given interface + * as a Collection of Types. + * + *

+ * + * See note in subclasses for information about the interface + * hierarchy. + */ + public Collection implementors(Type type) { + TypeNode node = (TypeNode) getImplementsNode(type); + + if (node != null) { + List list = new ArrayList(implementsGraph.preds(node)); + replaceTypeNodesWithTypes(list); + + return list; + } + + return new ArrayList(); + } + + /** + * Returns whether or not a is a subclass of b. + */ + public boolean subclassOf(Type a, Type b) { + // Is a <= b? + Assert.isTrue((a instanceof ReferenceType) + && (b instanceof ReferenceType), "Cannot compare " + a + + " and " + b); + + // a <= a: true + if (a.equals(b)) { + return true; + } + + // a <= java.lang.Object: true + if (b.equals(Type.OBJECT)) { + return true; + } + + // null <= null: true + // a <= null: false + if (b.equals(Type.NULL)) { + return a.equals(Type.NULL); + } + + if (a instanceof ArrayType) { + if (b instanceof ArrayType) { + ArrayType aArray = (ArrayType) a; + ArrayType bArray = (ArrayType) b; + // Both reference arrays. + // a <= b -> a[] <= b[] + if (aArray.getElementType() instanceof ReferenceType + && bArray.getElementType() instanceof ReferenceType) { + + return subclassOf(aArray.getElementType(), bArray + .getElementType()); + } + + // a[] <= a[]: true + return aArray.getElementType().equals(bArray.getElementType()); + } + + // Only one is an array (and b is not Object--tested above). + return false; + } + + // a <= b[]: false + if (b instanceof ArrayType) { + // Only one is an array. + return false; + } + + // Neither is an array. Look at all of the superclasses of a. If + // one of those superclasses is b, then a is a subclass of b. + for (Type t = a; t != null; t = superclass(t)) { + if (t.equals(b)) { + return true; + } + } + + return false; + } + + /** + * Returns (the Types of) all of the classes and interfaces in + * the hierarchy. + */ + public Collection classes() { + Assert.isTrue(classes != null); + return classes; + } + + /** + * Returns true if class closure has been computed + */ + public boolean closure() { + return (this.closure); + } + + /** + * Obtains a node from the extends graph. If it is not in the graph, we try + * to "bring it in". + */ + private TypeNode getExtendsNode(Type type) { + GraphNode node = extendsGraph.getNode(type); + + if (node == null && type instanceof ObjectType) { + this.addClassNamed(((ObjectType) type).getClassName()); + } + + return ((TypeNode) extendsGraph.getNode(type)); + } + + /** + * Obtains a node from the class graph. If it is not in the graph, we try to + * "bring it in". + */ + private TypeNode getImplementsNode(Type type) { + GraphNode node = implementsGraph.getNode(type); + + if (node == null && type instanceof ObjectType) { + this.addClassNamed(((ObjectType) type).getClassName()); + } + + return ((TypeNode) implementsGraph.getNode(type)); + } + + /** + * Adds a type (and all types it references) to the extends and implements + * graphs. + */ + private void addClass(String name) { + ObjectType type = new ObjectType(name); + + if (classes.contains(type)) { + return; + } + + if (inWorklist.contains(type)) { + return; + } + + db("ClassHierarchy: Adding " + name + " to hierarchy"); + + worklist.add(type); + inWorklist.add(type); + + while (!worklist.isEmpty()) { + type = (ObjectType) worklist.removeFirst(); + inWorklist.remove(type); + + if (classes.contains(type)) { + continue; + } + + classes.add(type); + + // Add a node in the extends graph for the type of interest + TypeNode extendsNode = (TypeNode) getExtendsNode(type); + + if (extendsNode == null) { + // Add a new node to the class graph + extendsNode = new TypeNode(type); + extendsGraph.addNode(type, extendsNode); + } + + // TypeNode implementsNode = (TypeNode) getImplementsNode(type); + + // if (implementsNode == null) { + // // Add a new node to the interface graph + // implementsNode = new TypeNode(type); + // implementsGraph.addNode(type, implementsNode); + // } + + // Obtain a ClassEditor for the class + JavaClass c; + + try { + c = context.loadClass(type.getClassName()); + + } catch (ClassNotFoundException ex) { + if (RELAX) { + continue; + } + + throw new RuntimeException("Class not found: " + + ex.getMessage()); + } + + String[] interfaces = c.getInterfaceNames(); + ObjectType zuper; + + try { + if (c.getSuperClass() == null) {// Bcel returns null for Object + zuper = new ObjectType("java.lang.Object"); + } else { + zuper = new ObjectType(c.getSuperclassName()); + } + } catch (ClassNotFoundException e) { + if (RELAX) + continue; + + throw new RuntimeException("Class not found: " + e.getMessage()); + } + + // Add an edge from the superclass to the class in the extends + // graph. + + if (!c.isInterface() || interfaces.length == 0) { + // Ignore interfaces that implement (really extend, see + // below) other interfaces. This way interfaces are put in + // the extends graph twice. + + TypeNode superNode = (TypeNode) getExtendsNode(zuper); + + if (superNode == null) { + superNode = new TypeNode(zuper); + extendsGraph.addNode(zuper, superNode); + } + + // Make sure that we're not making java.lang.Object a + // superclass of itself. We assume that the java.lang.Object + // has no successors in the extendsGraph. + if (!extendsNode.type + .equals(new ObjectType("java.lang.Object"))) { + extendsGraph.addEdge(extendsNode, superNode); + } + } + + // Consider the interfaces c implements + if (c.isInterface()) { + // Interfaces that extend other interfaces are compiled into + // classes that implement those other interfaces. So, + // interfaces that implement other interfaces really extend + // them. Yes, this makes the extends graph an inverted tree. + for (int i = 0; i < interfaces.length; i++) { + Type iType = new ObjectType(interfaces[i]); + TypeNode iNode = (TypeNode) getExtendsNode(iType); + if (iNode == null) { + iNode = new TypeNode(iType); + extendsGraph.addNode(iType, iNode); + } + extendsGraph.addEdge(extendsNode, iNode); + } + + } else { + // Class c implements its interfaces + TypeNode implementsNode = null; + if (interfaces.length > 0) { + implementsNode = getImplementsNode(type); + if (implementsNode == null) { + implementsNode = new TypeNode(type); + implementsGraph.addNode(type, implementsNode); + } + } + for (int i = 0; i < interfaces.length; i++) { + Type iType = new ObjectType(interfaces[i]); + TypeNode iNode = (TypeNode) getImplementsNode(iType); + if (iNode == null) { + iNode = new TypeNode(iType); + implementsGraph.addNode(iType, iNode); + } + implementsGraph.addEdge(implementsNode, iNode); + } + } + + if (zuper != null) { + // Add the super type to the worklist + + // db("typeref " + type + " -> " + c.superclass()); + + addType(zuper); + } + + for (int i = 0; i < interfaces.length; i++) { + // Add all of the interface types to the worklist + + // db("typeref " + type + " -> " + c.interfaces()[i]); + + addType(new ObjectType(interfaces[i])); + } + + // WE NEVER STARTED MODIFYING CLASSES SO DON"T NEED TO RELEASE + // THEM + // if(!this.closure) { + // context.release(c.classInfo()); + // continue; + // } + + for (int i = 0; i < c.getMethods().length; i++) { + // TODO: Add an enumeration to ClassEditor to get this list. + + // Add all of the methods types. Actually, we only add the + // type involved with the methods. + Method m = c.getMethods()[i]; + Type[] parameters = m.getArgumentTypes(); + for (int j = 0; j < parameters.length; j++) { + addType(parameters[j]); + } + addType(m.getReturnType()); + + // db("typeref " + type + " -> " + t); + + } + + for (int i = 0; i < c.getFields().length; i++) { + // Add the types of all of the fields + + Field f = c.getFields()[i]; + + Type t = f.getType(); + + // db("typeref " + type + " -> " + t); + + addType(t); + } + + for (int i = 0; i < c.getConstantPool().getLength(); i++) { + // Look through the constant pool for interesting (non-LONG + // and non-DOUBLE) constants and add them to the worklist. + + Constant conzt = c.getConstantPool().getConstant(i); + + if (conzt instanceof ConstantLong + || conzt instanceof ConstantDouble) { + i++; + + } else if (conzt instanceof ConstantClass) { + Type t = new ObjectType(c.getConstantPool() + .getConstantString(i, conzt.getTag())); + + // db("typeref " + type + " -> " + t); + + addType(t); + + } else if (conzt instanceof ConstantNameAndType) { + ConstantNameAndType conztNnT = (ConstantNameAndType) conzt; + + // db("typeref " + type + " -> " + t.type()); + + addType(Type.getReturnType(conztNnT.getSignature(c + .getConstantPool()))); + } + } + + // We're done editing the class + // WE NEVER STARTED EDITING THIS CLASS SO DON"T NEED TO RELEASE + // IT + // context.release(c.classInfo()); + + } + } + + // Adds a Type to the worklist. If the type is a method, then all + // of the parameters types and the return types are added. + private void addType(Type type) { + // I EXPANDED METHODS AS I RECIEVED THEM SO DON"T NEED THIS STEP + /* + * if (type.isMethod()) { // Add all of the types of the parameters and + * the return type + * + * Type[] paramTypes = type.paramTypes(); + * + * for (int i = 0; i < paramTypes.length; i++) { // db("typeref " + type + " -> " + + * paramTypes[i]); + * + * addType(paramTypes[i]); } + * + * Type returnType = type.returnType(); // db("typeref " + type + " -> " + + * returnType); + * + * addType(returnType); + * + * return; } + */ + if (type instanceof ArrayType) { + // TODO: Add the supertypes of the array and make it implement + // SERIALIZABLE and CLONEABLE. Since we're only concerned with + // fields and since arrays have no fields, we can ignore this + // for now. + + // db("typeref " + type + " -> " + type.elementType()); + + addType(((ArrayType) type).getElementType()); + // COULD ALSO BE: addType(type.getBasicType()); + + return; + } + + if (!(type instanceof ObjectType)) { + return; + } + + if (classes.contains(type)) { + return; + } + + if (inWorklist.contains(type)) { + return; + } + + worklist.add(type); + inWorklist.add(type); + } + + /** + * Returns the intersection of two types. Basically, the interstion of two + * types is the type (if any) to which both types may be assigned. So, if a + * is a subtype of b, a is returned. Otherwise, Type.NULL is + * returned. + */ + public Type intersectType(Type a, Type b) { + Assert.isTrue(a instanceof ReferenceType && b instanceof ReferenceType, + "Cannot intersect " + a + " and " + b); + + if (a.equals(b)) { + return a; + } + + if (a.equals(Type.NULL) || b.equals(Type.NULL)) { + return Type.NULL; + } + + if (a.equals(Type.OBJECT)) { + return b; + } + + if (b.equals(Type.OBJECT)) { + return a; + } + + if (a instanceof ArrayType) { + if (b instanceof ArrayType) { + ArrayType aArray = (ArrayType) a; + ArrayType bArray = (ArrayType) b; + + // Both reference arrays. + if (aArray.getElementType() instanceof ReferenceType + && bArray.getElementType() instanceof ReferenceType) { + + Type t = intersectType(aArray.getElementType(), bArray + .getElementType()); + + if (t.equals(Type.NULL)) { + return Type.NULL; + } + + return new ArrayType(t, aArray.getDimensions()); + } + + // Only one is a reference array. + if (aArray.getElementType() instanceof ReferenceType + || bArray.getElementType() instanceof ReferenceType) { + return Type.NULL; + } + + // Both primitive arrays, not equal. + return Type.NULL; + } + + // Only one is an array. + return Type.NULL; + } + + if (b instanceof ArrayType) { + // Only one is an array. + return Type.NULL; + } + + // Neither is an array. + + for (Type t = a; t != null; t = superclass(t)) { + if (t.equals(b)) { + // If a is a subtype of b, then return a. + return a; + } + } + + for (Type t = b; t != null; t = superclass(t)) { + if (t.equals(a)) { + // If b is a subtype of a, then return b + return b; + } + } + + return Type.NULL; + } + + /** + * Returns the most refined common supertype for a bunch of Types. + */ + public Type unionTypes(Collection types) { + if (types.size() <= 0) + return (new ObjectType("java.lang.Object")); + + Iterator ts = types.iterator(); + Type type = (Type) ts.next(); + + while (ts.hasNext()) { + type = this.unionType(type, (Type) ts.next()); + } + + return (type); + } + + /** + * Returns the union of two types. The union of two types is their most + * refined common supertype. At worst, the union is Type.OBJECT + */ + public Type unionType(Type a, Type b) { + + if (a.equals(b)) { + return a; + } + + if (a.equals(new ObjectType("java.lang.Object")) + || b.equals(new ObjectType("java.lang.Object"))) { + return new ObjectType("java.lang.Object"); + } + + if (a.equals(Type.NULL)) { + return b; + } + + if (b.equals(Type.NULL)) { + return a; + } + + // Handle funky integral types introduced during type inferencing. + if ((Assert.isIntegral(a) || a.equals(POS_BYTE) || a.equals(POS_SHORT)) + && (Assert.isIntegral(b) || b.equals(POS_BYTE) || b + .equals(POS_SHORT))) { + + BitSet v1 = typeToSet(a); + BitSet v2 = typeToSet(b); + v1.or(v2); + return (setToType(v1)); + } + + Assert.isTrue(a instanceof ReferenceType && b instanceof ReferenceType, + "Cannot union " + a + " and " + b); + + if (a instanceof ArrayType) { + if (b instanceof ArrayType) { + ArrayType aArray = (ArrayType) a; + ArrayType bArray = (ArrayType) b; + // Both reference arrays. + if (aArray.getElementType() instanceof ReferenceType + && bArray.getElementType() instanceof ReferenceType) { + + Type t = unionType(aArray.getElementType(), bArray + .getElementType()); + return new ArrayType(t, aArray.getDimensions()); + } + + // Only one is a reference array. + if (aArray.getElementType() instanceof ReferenceType + || bArray.getElementType() instanceof ReferenceType) { + return new ObjectType("java.lang.Object"); + } + + // Both primitive arrays, not equal. + return new ObjectType("java.lang.Object"); + } + + // Only one is an array. + return new ObjectType("java.lang.Object"); + } + + if (b instanceof ArrayType) { + // Only one is an array. + return new ObjectType("java.lang.Object"); + } + + // Neither is an array. + Set superOfA = new LinkedHashSet(); + Set superOfB = new LinkedHashSet(); + + for (Type t = a; t != null; t = superclass(t)) { + if (TypeInference.DEBUG) + System.out.println(" Superclass of " + a + " is " + t); + superOfA.add(t); + } + + for (Type t = b; t != null; t = superclass(t)) { + if (superOfA.contains(t)) { + return t; + } + + if (TypeInference.DEBUG) + System.out.println(" Superclass of " + b + " is " + t); + + superOfB.add(t); + } + + if (TypeInference.DEBUG) { + System.out.println("Superclasses of A: " + superOfA); + System.out.println("Superclasses of B: " + superOfB); + } + + for (Type t = a; t != null; t = superclass(t)) { + if (superOfB.contains(t)) { + // Found a common superclass... + return t; + } + } + + throw new RuntimeException("No common super type for " + a + " (" + + superOfA + ")" + " and " + b + " (" + superOfB + ")"); + } + + class TypeNode extends GraphNode { + Type type; + + public TypeNode(Type type) { + if (DEBUG) + System.out.println("Creating TypeNode for: " + type); + this.type = type; + } + + public String toString() { + return ("[" + type + "]"); + } + } + + /** + * Prints the class hierarchy (i.e. the "extends" hierarchy, interfaces may + * extends other interfaces) to a PrintWriter. + */ + public void printClasses(PrintWriter out, int indent) { + TypeNode objectNode = this.getExtendsNode(new ObjectType( + "java.lang.Object")); + indent(out, indent); + out.println(objectNode.type); + printSubclasses(objectNode.type, out, true, indent + 2); + } + + /** + * Prints the implements hierarchy to a PrintWriter. + */ + public void printImplements(PrintWriter out, int indent) { + // There are multiple roots to the implements graph. + indent += 2; + Iterator roots = this.implementsGraph.roots().iterator(); + while (roots.hasNext()) { + TypeNode iNode = (TypeNode) roots.next(); + indent(out, indent); + out.println(iNode.type); + printImplementors(iNode.type, out, true, indent + 2); + } + } + + /** + * Print the implementors of a given interface. Do we even have more than + * one level? + */ + private void printImplementors(Type iType, PrintWriter out, + boolean recurse, int indent) { + Iterator implementors = this.implementors(iType).iterator(); + while (implementors.hasNext()) { + Type implementor = (Type) implementors.next(); + indent(out, indent); + out.println(implementor); + if (recurse) + printImplementors(implementor, out, recurse, indent + 2); + } + } + + /** + * Prints a bunch of spaces to a PrintWriter. + */ + private void indent(PrintWriter out, int indent) { + for (int i = 0; i < indent; i++) + out.print(" "); + } + + /** + * Prints the subclasses of a given to a PrintWriter. + * + * @param recurse + * Are all subclasses printed or only direct ones? + */ + private void printSubclasses(Type classType, PrintWriter out, + boolean recurse, int indent) { + Iterator iter = this.subclasses(classType).iterator(); + while (iter.hasNext()) { + Type subclass = (Type) iter.next(); + indent(out, indent); + out.println(subclass); + if (recurse) + printSubclasses(subclass, out, recurse, indent + 2); + + } + } + + /** + * Determines whether or not a class's method is overriden by any of its + * subclasses. + */ + // I DON'T LIKE THAT NameAndType REFERS TO A METHOD AND THE TYPE IS A + // METHOD TYPE MAYBE CHANGE IT TO RECIEVE A MethodRef AND IGNORE + // DECLARING CLASS + public boolean methodIsOverridden(ObjectType classType, String methodName, + String signature) { + db("ClassHierarchy: Is " + classType + "." + methodName + signature + + " overridden?"); + + Collection subclasses = this.subclasses(classType); + + Iterator iter = subclasses.iterator(); + while (iter.hasNext()) { + ObjectType subclass = (ObjectType) iter.next(); + + db("Examining subclass " + subclass); + + // Obtain a ClassEditor for the class + JavaClass ce = null; + + try { + ce = context.loadClass(subclass.getClassName()); + + } catch (ClassNotFoundException ex) { + db(ex.getMessage()); + return (true); + } + + // Examine each method in the subclass + Method[] methods = ce.getMethods(); + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equals(methodName) + && methods[i].getSignature().equals(signature)) { + db(" " + methodName + signature + " is overridden by " + + methods[i].getName() + methods[i].getSignature()); + // context.release(ce.classInfo()); + // NEVER STARTED EDITING SO I DON'T HAVE TO RELEASE IT + return (true); // Method is overridden + } + } + + // Recurse over subclasses + if (methodIsOverridden(subclass, methodName, signature)) { + // context.release(ce.classInfo()); + // NEVER STARTED EDITING SO I DON'T HAVE TO RELEASE IT + return (true); + } + + // context.release(ce.classInfo()); + // NEVER STARTED EDITING SO I DON'T HAVE TO RELEASE IT + } + + // Got through all subclasses and method was not overridden + db(" NO!"); // FIXME: this message is fucking useless + + return (false); + } + + /** + * Returns the MemberRef of the method that would be invoked if a + * given method of a given type was invoked. Basically, dynamic dispatch is + * simulated. + */ + // NOT USED IN BLOAT + /* + * public MemberRef methodInvoked(Type receiver, NameAndType method) { // + * Search up class hierarchy for a class that implements the // method + * for(Type type = receiver; type != null; type = superclass(type)) { // + * Construct a MemberRef for the possible method MemberRef m = new + * MemberRef(type, method); try { context.editMethod(m); return(m); } + * catch(NoSuchMethodException ex) { continue; // Try superclass } } // Hmm. + * No superclass method was found! throw new IllegalArgumentException("No + * implementation of " + receiver + "." + method); } + */ + /** + * Given a set of bits representing the range of values some type has, + * determines what that Type is. + */ + public static Type setToType(BitSet v) { + if (v.get(MAX_INT)) { + return Type.INT; + } + + if (v.get(MAX_CHAR)) { + if (v.get(MIN_INT) || v.get(MIN_SHORT) || v.get(MIN_BYTE)) { + return Type.INT; + + } else { + return Type.CHAR; + } + } + + if (v.get(MAX_SHORT)) { + if (v.get(MIN_INT)) { + return Type.INT; + + } else if (v.get(MIN_SHORT) || v.get(MIN_BYTE)) { + return Type.SHORT; + + } else { + return POS_SHORT; + } + } + + if (v.get(MAX_BYTE)) { + if (v.get(MIN_INT)) { + return Type.INT; + + } else if (v.get(MIN_SHORT)) { + return Type.SHORT; + + } else if (v.get(MIN_BYTE)) { + return Type.BYTE; + + } else { + return POS_BYTE; + } + } + + if (v.get(MAX_BOOL)) { + if (v.get(MIN_INT)) { + return Type.INT; + + } else if (v.get(MIN_SHORT)) { + return Type.SHORT; + + } else if (v.get(MIN_BYTE)) { + return Type.BYTE; + + } else { + return Type.BOOLEAN; + } + } + + if (v.get(MIN_INT)) { + return Type.INT; + } + + if (v.get(MIN_SHORT)) { + return Type.SHORT; + } + + if (v.get(MIN_BYTE)) { + return Type.BYTE; + } + + return Type.BOOLEAN; + } + + /** + * Returns a BitSet representing the possible values of a given integral + * type. + */ + public static BitSet typeToSet(Type type) { + BitSet v = new BitSet(MAX_INT); + int lo; + int hi; + + if (type.equals(Type.INT)) { + lo = MIN_INT; + hi = MAX_INT; + + } else if (type.equals(Type.CHAR)) { + lo = MIN_CHAR; + hi = MAX_CHAR; + + } else if (type.equals(Type.SHORT)) { + lo = MIN_SHORT; + hi = MAX_SHORT; + + } else if (type.equals(POS_SHORT)) { + lo = ZERO; + hi = MAX_SHORT; + + } else if (type.equals(Type.BYTE)) { + lo = MIN_BYTE; + hi = MAX_BYTE; + + } else if (type.equals(POS_BYTE)) { + lo = ZERO; + hi = MAX_BYTE; + + } else if (type.equals(Type.BOOLEAN)) { + lo = ZERO; + hi = MAX_BOOL; + + } else { + throw new RuntimeException(); + } + + for (int i = lo; i <= hi; i++) { + v.set(i); + } + + return v; + } + + /** + * Represents a method and a set of Types. When the method is + * invoked on a receiver of any of these types, the method will resolve to + * that method. + */ + public class ResolvesToWith { + /** + * The method to which a call resolves + */ + public MethodRef method; + + /** + * The types with which the call resolves to the above method + */ + public LinkedHashSet rTypes; + } + + /** + * Returns a set of ResolvesToWith that represent all subclass + * methods that override a given method and the subclasses that when used as + * receivers resolve to that method. + * + * @see ResolvesToWith + */ + public Set resolvesToWith(MethodRef method) { + Set resolvesTo = (Set) this.resolvesToCache.get(method); + + if (resolvesTo == null) { + db("Resolving " + method); + + resolvesTo = new LinkedHashSet(); // All methods it could resolve + // to + ResolvesToWith rtw = new ResolvesToWith(); + rtw.method = method; + rtw.rTypes = new LinkedHashSet(); + + // Remember that the method may be abstract, so the declaring + // class is not necessarily a resolving type. + + // Basically, we go down the class and/or interface hierarchies + // looking for concrete implementations of this method. + // JavaClass jc = null; + Method me = null; + try { + me = context.editMethod(method).getMethod(); + } catch (NoSuchMethodException ex1) { + // A method may not necessarily be implemented by its + // declaring class. For instance, an abstract class that + // implements an interface need not implement every method of + // the interface. + db(" Hmm. Method is not implemented in declaring class"); + } + + // If the method is static or is a constructor, then it can only + // resolve to itself. + if (me != null && (me.isStatic() || me.getName().equals("init"))) { + rtw.rTypes.add(method.declaringClass()); + resolvesTo.add(rtw); + db(" Static method or constructor, resolves to itself"); + + } else { + // Now let's play with types. Examine every type that could + // implement this method. If it does, add it to the resolvesTo + // set. Make sure to take things like interfaces into account. + // When we find a overriding method, make a recursive call so + // we'll have that information in the cache. + List types = new LinkedList(); + types.add(method.declaringClass()); + while (!types.isEmpty()) { + ObjectType type = (ObjectType) types.remove(0); + + db(" Examining type " + type); + + JavaClass ce = null; + try { + ce = context.loadClass(type.getClassName()); + + } catch (ClassNotFoundException ex1) { + System.err.println("** Class not found: " + + ex1.getMessage()); + ex1.printStackTrace(System.err); + System.exit(1); + } + + if (ce.isInterface()) { + // Consider all subinterfaces of this interface and all + // classes that implement this interface. + Iterator subinterfaces = this.subclasses(type) + .iterator(); + while (subinterfaces.hasNext()) { + Type subinterface = (Type) subinterfaces.next(); + types.add(subinterface); + db(" Noting subinterface " + subinterface); + } + + Iterator implementors = this.implementors(type) + .iterator(); + while (implementors.hasNext()) { + Type implementor = (Type) implementors.next(); + types.add(implementor); + db(" Noting implementor " + implementor); + } + + } else { + // We've got a class. Does it override the method? + Method[] methods = ce.getMethods(); + boolean overridden = false; + for (int i = 0; i < methods.length; i++) { + if (method.getSignature().equals( + methods[i].getSignature())) { + // This class implements the method. + if (!method.declaringClass().equals(type)) { + // Make a recursive call. + db(" Class " + type + " overrides " + + method); + MethodRef mR = new MethodRef(type, + new NameAndType(methods[i] + .getName(), methods[i] + .getReturnType()), + methods[i].getReturnType(), + methods[i].getArgumentTypes()); + + resolvesTo.addAll(resolvesToWith(mR)); + overridden = true; + } + } + } + + if (!overridden) { + db(" " + rtw.method + " called with " + type); + rtw.rTypes.add(type); + resolvesTo.add(rtw); + + // Examine all subclasses of this class. They + // may override + // the method also. + Iterator subclasses = this.subclasses(type) + .iterator(); + while (subclasses.hasNext()) { + Type subclass = (Type) subclasses.next(); + types.add(subclass); + db(" Noting subclass " + subclass); + } + } + } + } + } + + resolvesToCache.put(method, resolvesTo); + } + + return (resolvesTo); + } +} diff --git a/src/edu/purdue/cs/bloat/editor/EditorContext.java b/src/edu/purdue/cs/bloat/editor/EditorContext.java new file mode 100644 index 0000000..faac507 --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/EditorContext.java @@ -0,0 +1,179 @@ +/* + * Class: EditorContext + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +/** + * An EditorContext supplies a means of loading and editing classes. + * Note that a number of these methods are identical to methods in + * Editor. It is expected that an EditorContext will have + * a different caching (of ClassEditors, etc.) policy than + * Editor does. Hence, the methods in EditorContext should + * be used to edit classes, etc. + */ +public interface EditorContext { + + /** + * Loads a class into BLOAT + */ + public JavaClass loadClass(String className) throws ClassNotFoundException; + + /** + * Creates a new ClassInfo + * + * @param modifiers + * The modifiers describing the newly-created class + * @param classIndex + * The index of the name of the newly-created class in its + * constant pool + * @param superClassIndex + * The index of the name of the newly-created class's superclass + * in its constant pool + * @param interfaceIndexes + * The indexes of the names of the interfaces that the + * newly-created class implements + * @param constants + * The constant pool for the newly created class (a list of + * {@link Constant}s). + */ + /** + * Returns the ClassHierarchy of all classes and interfaces known + * to BLOAT. + */ + public ClassHierarchy getHierarchy(); + + public JavaClass newClassInfo(int class_name_index, + int superclass_name_index, String file_name, int major, int minor, + int access_flags, ConstantPool constant_pool, int[] interfaces, + Field[] fields, Method[] methods, Attribute[] attributes); + + /** + * Returns a ClassEditor for editing a new class with the + * given name. It will override any class with the given name that is + * already being edited. + */ + + public ClassGen newClass(int accessflags, String className, + String file_name, Type superType, Type[] interfaces); + + /** + * Returns a ClassEditor used to edit a class of a given name. + */ + public ClassGen editClass(String className) throws ClassNotFoundException, + ClassFormatException; + + /** + * Returns a ClassEditor used to edit a class described by a + * given Type. + */ + public ClassGen editClass(Type classType) throws ClassNotFoundException, + ClassFormatException; + + /** + * Returns a ClassEditor used to edit a class described by a + * given ClassInfo. + */ + public ClassGen editClass(JavaClass info); + + /** + * Returns a FieldEditor for editing a FieldInfo. + */ + public FieldGen editField(Field info, String classname); + + /** + * Returns a FieldEditor for editing a field. + */ + public FieldGen editField(MemberRef field) throws NoSuchFieldException; + + /** + * Returns a MethodEditor for editing a method. + */ + public MethodGen editMethod(Method info, String classname); + + /** + * Returns a MethodEditor for editing a method. + */ + public MethodGen editMethod(MethodRef method) throws NoSuchMethodException; + + /** + * Signals that we are done editing a method. The object used to model it + * may be reclaimed. + */ + public void release(Method info, String className); + + /** + * Signals that we are done editing a field. The object used to model it may + * be reclaimed. + */ + public void release(Field info, String className); + + /** + * Signals that we are done editing a class. The object used to model it may + * be reclaimed. + */ + public void release(JavaClass info); + + /** + * Commits the changes made to a class. + */ + public void commit(JavaClass info); + + /** + * Commits the changes made to a method. + */ + public void commit(Method info, String className); + + /** + * Commits the changes made to a method. + */ + public void commit(MethodGen info); + + /** + * Commits the changes made to a field. + */ + public void commit(Field info, String className); + + /** + * Commits all changes made to classes, methods, and fields. + */ + public void commit(); +} diff --git a/src/edu/purdue/cs/bloat/editor/InstructionAdapter.java b/src/edu/purdue/cs/bloat/editor/InstructionAdapter.java new file mode 100644 index 0000000..fbf9aec --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/InstructionAdapter.java @@ -0,0 +1,480 @@ +/* + * Class: InstructionAdaptor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.*; + +/** + * This adapter provides a default implementation for every method in + * InstructionVisitor. + */ +public class InstructionAdapter { + public void visit_nop(Instruction inst) { + } + + public void visit_ldc(Instruction inst) { + } + + public void visit_iload(Instruction inst) { + } + + public void visit_lload(Instruction inst) { + } + + public void visit_fload(Instruction inst) { + } + + public void visit_dload(Instruction inst) { + } + + public void visit_aload(Instruction inst) { + } + + public void visit_iaload(Instruction inst) { + } + + public void visit_laload(Instruction inst) { + } + + public void visit_faload(Instruction inst) { + } + + public void visit_daload(Instruction inst) { + } + + public void visit_aaload(Instruction inst) { + } + + public void visit_baload(Instruction inst) { + } + + public void visit_caload(Instruction inst) { + } + + public void visit_saload(Instruction inst) { + } + + public void visit_istore(Instruction inst) { + } + + public void visit_lstore(Instruction inst) { + } + + public void visit_fstore(Instruction inst) { + } + + public void visit_dstore(Instruction inst) { + } + + public void visit_astore(Instruction inst) { + } + + public void visit_iastore(Instruction inst) { + } + + public void visit_lastore(Instruction inst) { + } + + public void visit_fastore(Instruction inst) { + } + + public void visit_dastore(Instruction inst) { + } + + public void visit_aastore(Instruction inst) { + } + + public void visit_bastore(Instruction inst) { + } + + public void visit_castore(Instruction inst) { + } + + public void visit_sastore(Instruction inst) { + } + + public void visit_pop(Instruction inst) { + } + + public void visit_pop2(Instruction inst) { + } + + public void visit_dup(Instruction inst) { + } + + public void visit_dup_x1(Instruction inst) { + } + + public void visit_dup_x2(Instruction inst) { + } + + public void visit_dup2(Instruction inst) { + } + + public void visit_dup2_x1(Instruction inst) { + } + + public void visit_dup2_x2(Instruction inst) { + } + + public void visit_swap(Instruction inst) { + } + + public void visit_iadd(Instruction inst) { + } + + public void visit_ladd(Instruction inst) { + } + + public void visit_fadd(Instruction inst) { + } + + public void visit_dadd(Instruction inst) { + } + + public void visit_isub(Instruction inst) { + } + + public void visit_lsub(Instruction inst) { + } + + public void visit_fsub(Instruction inst) { + } + + public void visit_dsub(Instruction inst) { + } + + public void visit_imul(Instruction inst) { + } + + public void visit_lmul(Instruction inst) { + } + + public void visit_fmul(Instruction inst) { + } + + public void visit_dmul(Instruction inst) { + } + + public void visit_idiv(Instruction inst) { + } + + public void visit_ldiv(Instruction inst) { + } + + public void visit_fdiv(Instruction inst) { + } + + public void visit_ddiv(Instruction inst) { + } + + public void visit_irem(Instruction inst) { + } + + public void visit_lrem(Instruction inst) { + } + + public void visit_frem(Instruction inst) { + } + + public void visit_drem(Instruction inst) { + } + + public void visit_ineg(Instruction inst) { + } + + public void visit_lneg(Instruction inst) { + } + + public void visit_fneg(Instruction inst) { + } + + public void visit_dneg(Instruction inst) { + } + + public void visit_ishl(Instruction inst) { + } + + public void visit_lshl(Instruction inst) { + } + + public void visit_ishr(Instruction inst) { + } + + public void visit_lshr(Instruction inst) { + } + + public void visit_iushr(Instruction inst) { + } + + public void visit_lushr(Instruction inst) { + } + + public void visit_iand(Instruction inst) { + } + + public void visit_land(Instruction inst) { + } + + public void visit_ior(Instruction inst) { + } + + public void visit_lor(Instruction inst) { + } + + public void visit_ixor(Instruction inst) { + } + + public void visit_lxor(Instruction inst) { + } + + public void visit_iinc(Instruction inst) { + } + + public void visit_i2l(Instruction inst) { + } + + public void visit_i2f(Instruction inst) { + } + + public void visit_i2d(Instruction inst) { + } + + public void visit_l2i(Instruction inst) { + } + + public void visit_l2f(Instruction inst) { + } + + public void visit_l2d(Instruction inst) { + } + + public void visit_f2i(Instruction inst) { + } + + public void visit_f2l(Instruction inst) { + } + + public void visit_f2d(Instruction inst) { + } + + public void visit_d2i(Instruction inst) { + } + + public void visit_d2l(Instruction inst) { + } + + public void visit_d2f(Instruction inst) { + } + + public void visit_i2b(Instruction inst) { + } + + public void visit_i2c(Instruction inst) { + } + + public void visit_i2s(Instruction inst) { + } + + public void visit_lcmp(Instruction inst) { + } + + public void visit_fcmpl(Instruction inst) { + } + + public void visit_fcmpg(Instruction inst) { + } + + public void visit_dcmpl(Instruction inst) { + } + + public void visit_dcmpg(Instruction inst) { + } + + public void visit_ifeq(Instruction inst) { + } + + public void visit_ifne(Instruction inst) { + } + + public void visit_iflt(Instruction inst) { + } + + public void visit_ifge(Instruction inst) { + } + + public void visit_ifgt(Instruction inst) { + } + + public void visit_ifle(Instruction inst) { + } + + public void visit_if_icmpeq(Instruction inst) { + } + + public void visit_if_icmpne(Instruction inst) { + } + + public void visit_if_icmplt(Instruction inst) { + } + + public void visit_if_icmpge(Instruction inst) { + } + + public void visit_if_icmpgt(Instruction inst) { + } + + public void visit_if_icmple(Instruction inst) { + } + + public void visit_if_acmpeq(Instruction inst) { + } + + public void visit_if_acmpne(Instruction inst) { + } + + public void visit_goto(Instruction inst) { + } + + public void visit_jsr(Instruction inst) { + } + + public void visit_ret(Instruction inst) { + } + + public void visit_switch(Instruction inst) { + } + + public void visit_ireturn(Instruction inst) { + } + + public void visit_lreturn(Instruction inst) { + } + + public void visit_freturn(Instruction inst) { + } + + public void visit_dreturn(Instruction inst) { + } + + public void visit_areturn(Instruction inst) { + } + + public void visit_return(Instruction inst) { + } + + public void visit_getstatic(Instruction inst) { + } + + public void visit_putstatic(Instruction inst) { + } + + public void visit_putstatic_nowb(Instruction inst) { + } + + public void visit_getfield(Instruction inst) { + } + + public void visit_putfield(Instruction inst) { + } + + public void visit_putfield_nowb(Instruction inst) { + } + + public void visit_invokevirtual(Instruction inst) { + } + + public void visit_invokespecial(Instruction inst) { + } + + public void visit_invokestatic(Instruction inst) { + } + + public void visit_invokeinterface(Instruction inst) { + } + + public void visit_new(Instruction inst) { + } + + public void visit_newarray(Instruction inst) { + } + + public void visit_arraylength(Instruction inst) { + } + + public void visit_athrow(Instruction inst) { + } + + public void visit_checkcast(Instruction inst) { + } + + public void visit_instanceof(Instruction inst) { + } + + public void visit_monitorenter(Instruction inst) { + } + + public void visit_monitorexit(Instruction inst) { + } + + public void visit_multianewarray(Instruction inst) { + } + + public void visit_ifnull(Instruction inst) { + } + + public void visit_ifnonnull(Instruction inst) { + } + + public void visit_rc(Instruction inst) { + } + + public void visit_aupdate(Instruction inst) { + } + + public void visit_supdate(Instruction inst) { + } + + public void visit_aswizzle(Instruction inst) { + } + + public void visit_aswrange(Instruction inst) { + } +} diff --git a/src/edu/purdue/cs/bloat/editor/InstructionVisitor.java b/src/edu/purdue/cs/bloat/editor/InstructionVisitor.java new file mode 100644 index 0000000..351533f --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/InstructionVisitor.java @@ -0,0 +1,70 @@ +/* + * Class: InstructionVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.Instruction; + +/** + * The visitor pattern allows functionality to be added to a number of classes + * (or in this case one class, Instruction, that can vary in + * behavior) without modifying the classes themselves. Additionally, the visitor + * pattern simulates double dispatching. For instance visit method of + * Instruction calls a particular method of + * InstructionVisitor based on the Instruction's opcode. + *

+ * InstructionVisitor provides an interface for performing actions + * based on the instruction type. Classes implementing this interface should not + * be able to miss any of the instruction types. This interface was created as + * an alternative to having 138 different subtypes of Instruction. + * + * @see Instruction#visit + * @see edu.purdue.cs.bloat.tree.Tree + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface InstructionVisitor { + public void visitInst(Instruction inst); + // public void visit_rc(Instruction inst); + // public void visit_aupdate(Instruction inst); + // public void visit_supdate(Instruction inst); + // public void visit_aswizzle(Instruction inst); + // public void visit_aswrange(Instruction inst); +} diff --git a/src/edu/purdue/cs/bloat/editor/Makefile b/src/edu/purdue/cs/bloat/editor/Makefile new file mode 100644 index 0000000..5150dc8 --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/Makefile @@ -0,0 +1,31 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + ClassHierarchy.class\ + EditorContext.class\ + InstructionAdapter.class\ + InstructionVisitor.class\ + MemberRef.class\ + MethodRef.class\ + NameAndType.class\ + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/editor/MemberRef.java b/src/edu/purdue/cs/bloat/editor/MemberRef.java new file mode 100644 index 0000000..662505c --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/MemberRef.java @@ -0,0 +1,143 @@ +/* + * Class: MemberRef + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.*; + +/** + * MemberRef represents a method or field (as a NameAndType) and the + * class (as a Type) in which it is declared. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + * + * I found it rather confusing having MemberRef refer to both fields and methods + * so I have subclassed MemberRef with MethodRef for use when refering to + * methods. --Arrin + * @see MethodRef + */ +public class MemberRef { + private final ReferenceType declaringClass; + + private final NameAndType nameAndType; + + /** + * Constructor. + * + * @param declaringClass + * The type of the class which declared the member. + * @param nameAndType + * The name and type of the member. + */ + public MemberRef(ReferenceType declaringClass, NameAndType nameAndType) { + this.declaringClass = declaringClass; + this.nameAndType = nameAndType; + } + + /** + * Get the type of the class which declared the member. + * + * @return The type of the class which declared the member. + */ + public ReferenceType declaringClass() { + return declaringClass; + } + + /** + * Get the name of the member. + * + * @return The name of the member. + */ + public String name() { + return nameAndType.name(); + } + + /** + * Get the type of the member. + * + * @return The type of the member. + */ + public Type type() { + return nameAndType.type(); + } + + /** + * Get the name and type of the member. + * + * @return The name and type of the member. + */ + public NameAndType nameAndType() { + return nameAndType; + } + + /** + * Convert the reference to a string. + * + * @return A string representation of the reference. + */ + public String toString() { + // Take advantage of PRINT_TRUNCATED in Type + String className = declaringClass.toString(); + return "<" + "Field" + " " + className + "." + name() + " " + type() + + ">"; + } + + /** + * Check if an object is equal to this reference. + * + * @param obj + * The object to compare against. + * @return true if equal, false if not. + */ + public boolean equals(Object obj) { + return obj instanceof MemberRef + && ((MemberRef) obj).declaringClass.equals(declaringClass) + && ((MemberRef) obj).nameAndType.equals(nameAndType); + } + + /** + * Hash the member reference. + * + * @return The hash code. + */ + public int hashCode() { + return declaringClass.hashCode() ^ nameAndType.hashCode(); + } +} diff --git a/src/edu/purdue/cs/bloat/editor/MethodRef.java b/src/edu/purdue/cs/bloat/editor/MethodRef.java new file mode 100644 index 0000000..570e865 --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/MethodRef.java @@ -0,0 +1,117 @@ +/* + * Class: MethodRef + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.*; + +/** + * MemberRef represents a method or field (as a NameAndType) and the + * class (as a Type) in which it is declared. + * + * @author Arrin Daley (arrin@cs.anu.edu.au) + * + * I found it rather confusing having MemberRef refer to both fields and methods + * so I have subclassed MemberRef with MethodRef for use when refering to + * methods. --Arrin + * @see MemberRef + */ + +/* + * BCEL types only refer to a field or object type but BLOAT types can refer to + * both a method and a field as a result I made this class to store Method + * reference information + */ + +public class MethodRef extends MemberRef { + + private final Type[] params; + + private final Type returnType; + + public MethodRef(ReferenceType declaringClass, NameAndType nameAndType, + Type returnType, Type[] paramTypes) { + super(declaringClass, nameAndType); + this.params = paramTypes; + this.returnType = returnType; + } + + public Type[] paramTypes() { + return params; + } + + public Type returnType() { + return returnType; + } + + public Type type() { + return returnType; + } + + public boolean equals(Object obj) { + if (super.equals(obj) && obj instanceof MethodRef) { + MethodRef methRef = (MethodRef) obj; + Type[] paramTest = methRef.paramTypes(); + if (returnType.equals(methRef.returnType()) + && params.length == paramTest.length) { + for (int i = 0; i < params.length; i++) { + if (!params[i].equals(paramTest[i])) + return false; + } + } else { + return false; + } + } else + return false; + + return true; + } + + public String toString() { + // Take advantage of PRINT_TRUNCATED in Type + String className = declaringClass().toString(); + return "<" + "Method" + " " + className + "." + name() + " " + + Type.getMethodSignature(returnType, params) + ">"; + } + + public String getSignature() { + return Type.getMethodSignature(returnType, params); + } +} diff --git a/src/edu/purdue/cs/bloat/editor/NameAndType.java b/src/edu/purdue/cs/bloat/editor/NameAndType.java new file mode 100644 index 0000000..95550e3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/NameAndType.java @@ -0,0 +1,112 @@ +/* + * Class: NameAndType + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.editor; + +import org.apache.bcel.generic.Type; + +/** + * Methods and fields are described by their name and type descriptor. + * NameAndType represents exactly that. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + * + * In BLOAT NameAndType could refer to both Methods and Fields using MemberRef, + * Type being used to encapsulate the parameters and return type of the method. + * Types in BCEL can't do this so MethodRef is used for methods and MemberRef is + * used for fields. NameAndType is still used to refer to Methods but only + * encapsulates the name and return type of the method parameters are held + * inside MethodRef. --Arrin + */ + +public class NameAndType { + private final String name; + + private final Type type; + + /** + * Constructor. + */ + public NameAndType(String name, Type type) { + this.name = name; + this.type = type; + } + + /** + * Returns the name. + */ + public String name() { + return name; + } + + /** + * Returns the type. + */ + public Type type() { + return type; + } + + /** + * Returns a string representation of the name and type. + */ + public String toString() { + return ""; + } + + /** + * Check if an object is equal to this name and type. + * + * @param obj + * The object to compare against. + * @return true if equal + */ + public boolean equals(Object obj) { + return obj instanceof NameAndType + && ((NameAndType) obj).name.equals(name) + && ((NameAndType) obj).type.equals(type); + } + + /** + * Returns a hash of the name and type. + */ + public int hashCode() { + return name.hashCode() ^ type.hashCode(); + } +} diff --git a/src/edu/purdue/cs/bloat/editor/package.html b/src/edu/purdue/cs/bloat/editor/package.html new file mode 100644 index 0000000..0a05e81 --- /dev/null +++ b/src/edu/purdue/cs/bloat/editor/package.html @@ -0,0 +1,10 @@ + + + +

Allows classes, methods, and fields to be edited. In addition, the +representation of bytecodes (JVM instructions) is refined. Types, +opcodes, local variables, and special instrcution operands are +introduced.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/inline/CallGraph.java b/src/edu/purdue/cs/bloat/inline/CallGraph.java new file mode 100644 index 0000000..744556a --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/CallGraph.java @@ -0,0 +1,853 @@ +/* + * Class: CallGraph + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.inline; + +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +import edu.purdue.cs.bloat.editor.ClassHierarchy; +import edu.purdue.cs.bloat.editor.InstructionAdapter; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.editor.NameAndType; +import edu.purdue.cs.bloat.util.Assert; + +import java.io.*; +import java.util.*; + +/** + * Grants access to certain information about a Java program. At least one root + * method must be specified. From these root methods, the call graph and + * information such as the classes that are instantiated during the Java program + * is computed. + * + *

+ * + * The construction of the call graph is in the spirit of the "Program + * Virtual-call Graph" presented in [Bacon97]. However, certain changes have + * been made to tailor it to BLOAT and Java and to make the overall + * representation smaller. + * + *

+ * + * Rapid type analysis is integrated into the construction of the call graph. A + * virtual method is not examined until we know that its declaring class has + * been instantiated. + * + *

+ * + * Some classes are created internally by the VM and are missed by our analysis. + * So, we maintain a set of "pre-live" classes. We consider all of their + * constructors to be live. + */ +public class CallGraph { + public static boolean DEBUG = false; + + private static Set preLive; // "Pre-live" classes + + public static boolean USEPRELIVE = true; + + public static boolean USE1_2 = true; + + private Set roots; // Root methods (MethodRefs) + + private Map calls; // Maps methods to the methods they + + // call (virtual calls are not resolved) + private Set liveClasses; // Classes (Types) that have been instantiated + + private Map resolvesTo; // Maps methods to the methods they resolve to + + private Map blocked; // Maps types to methods blocked on those types + + List worklist; // Methods to process + + Set liveMethods; // Methods that may be executed + + InlineContext context; + + private ClassHierarchy hier; + + static void db(String s) { + if (CallGraph.DEBUG) + System.out.println(s); + } + + /** + * Initialize the set of classes that are "pre-live" + */ + private static void init() { + // We can't do this in the static initializer because USE1_2 might + // not have the desired value. + + preLive = new LinkedHashSet(); + + preLive.add("java.lang.Boolean"); + preLive.add("java.lang.Class"); + preLive.add("java.lang.ClassLoader"); + preLive.add("java.lang.Compiler"); + preLive.add("java.lang.Integer"); + preLive.add("java.lang.SecurityManager"); + preLive.add("java.lang.String"); + preLive.add("java.lang.StringBuffer"); + preLive.add("java.lang.System"); + preLive.add("java.lang.StackOverflowError"); + preLive.add("java.lang.Thread"); + preLive.add("java.lang.ThreadGroup"); + + preLive.add("java.io.BufferedInputStream"); + preLive.add("java.io.BufferedReader"); + preLive.add("java.io.BufferedOutputStream"); + preLive.add("java.io.BufferedWriter"); + preLive.add("java.io.File"); + preLive.add("java.io.FileDescriptor"); + preLive.add("java.io.InputStreamReader"); + preLive.add("java.io.ObjectStreamClass"); + preLive.add("java.io.OutputStreamWriter"); + preLive.add("java.io.PrintStream"); + preLive.add("java.io.PrintWriter"); + + preLive.add("java.net.URL"); + + preLive.add("java.security.Provider"); + preLive.add("java.security.Security"); + + preLive.add("java.util.Hashtable"); + preLive.add("java.util.ListResourceBundle"); + preLive.add("java.util.Locale"); + preLive.add("java.util.Properties"); + preLive.add("java.util.Stack"); + preLive.add("java.util.Vector"); + + preLive.add("java.util.zip.ZipFile"); + + // Some pre-live classes are only available on JDK1.2. + if (USE1_2) { + preLive.add("java.lang.Package"); + + preLive.add("java.lang.ref.Finalizer"); + preLive.add("java.lang.ref.ReferenceQueue"); + + preLive.add("java.io.FilePermission"); + preLive.add("java.io.UnixFileSystem"); + + preLive.add("java.net.URLClassLoader"); + + preLive.add("java.security.SecureClassLoader"); + preLive.add("java.security.AccessController"); + + preLive.add("java.text.resources.LocaleElements"); + preLive.add("java.text.resources.LocaleElements_en"); + + preLive.add("java.util.LinkedHashMap"); + + preLive.add("java.util.jar.JarFile"); + } + } + + /** + * Adds (the name of) a class to the set of classes that are considered to + * be "pre-live" + */ + public static void addPreLive(String name) { + if (preLive == null) { + init(); + } + preLive.add(name); + } + + /** + * Removes a class from the set of "pre-live" classes + * + * @return true if the class was "pre-live" + */ + public static boolean removePreLive(String name) { + if (preLive == null) { + init(); + } + return (preLive.remove(name)); + } + + /** + * Constructor. + * + * @param context + * InlineContext used to examine classes and methods. + * + * @param roots + * The methods (represented as MemberRefs) considered + * to the roots (that is, the "main" methods) of the call graph. + * Presumably, only static methods or constructors can be root + * methods. + */ + public CallGraph(InlineContext context, Set roots) { + Assert.isTrue(roots != null, "A call graph must have roots"); + Assert.isTrue(roots.size() > 0, "A call graph must have roots"); + + if (preLive == null) { + init(); + } + + this.context = context; + this.hier = context.getHierarchy(); + this.roots = roots; + + this.liveClasses = new LinkedHashSet(); + this.resolvesTo = new LinkedHashMap(); + this.calls = new LinkedHashMap(); + this.blocked = new LinkedHashMap(); + this.worklist = new LinkedList(this.roots); + this.liveMethods = new LinkedHashSet(); + + // To save space, make one InstructionVisitor and use it on every + // Instruction. + CallVisitor visitor = new CallVisitor(this); + + db("Adding pre-live classes"); + doPreLive(); + + db("Constructing call graph"); + + // Examine each method in the worklist. At each constructor + // invocation make note of the type that was created. At each + // method call determine all possible methods that it can resolve + // to. Add the methods of classes that have been instantiated to + // the worklist. + while (!worklist.isEmpty()) { + MethodRef caller = (MethodRef) worklist.remove(0); + + if (liveMethods.contains(caller)) { + // We've already handled this method + continue; + } + + MethodGen callerMethod = null; + try { + callerMethod = context.editMethod(caller); + + } catch (NoSuchMethodException ex1) { + System.err.println("** Could not find method: " + caller); + ex1.printStackTrace(System.err); + System.exit(1); + } + + // If the method is abstract or native, we can't do anything + // with it. + if (callerMethod.isAbstract()) { + continue; + } + + liveMethods.add(caller); + + if (callerMethod.isNative()) { + // We still want native methods to be live + continue; + } + + db("\n Examining method " + caller); + + Set callees = new LinkedHashSet(); // Methods called by caller + calls.put(caller, callees); + + // If the method is static or is a constructor, the classes + // static initializer method must have been called. Make note + // of this. + if (callerMethod.isStatic() + || callerMethod.getName().equals("")) { + addClinit(new ObjectType(callerMethod.getClassName())); + } + + // Examine the instructions in the caller method. + Iterator code = callerMethod.getInstructionList().iterator(); + visitor.setCaller(callerMethod); + while (code.hasNext()) { + InstructionHandle o = (InstructionHandle) code.next(); + Instruction inst = o.getInstruction(); + visitor.visit(inst, callerMethod.getConstantPool()); + } + } + + // We're done constructing the call graph. Try to free up some + // memory. + blocked = null; + } + + /** + * Helper method to add the static initializers and all constructors of the + * pre-live classes to the worklist, etc. + */ + private void doPreLive() { + if (!USEPRELIVE) { + return; + } + + db("Making constructors of pre-live classes live"); + + Iterator iter = preLive.iterator(); + while (iter.hasNext()) { + String name = (String) iter.next(); + db(" " + name + " is pre-live"); + name = name.replace('.', '/'); + + ClassGen ce = null; + try { + ce = context.editClass(name); + + } catch (ClassNotFoundException ex1) { + System.err.println("** Cannot find pre-live class: " + name); + ex1.printStackTrace(System.err); + System.exit(1); + } + + // Make class and static initializer live + liveClasses.add(new ObjectType(ce.getClassName())); + addClinit(new ObjectType(ce.getClassName())); + + // Make all constructors live + Method[] methods = ce.getMethods(); + for (int i = 0; i < methods.length; i++) { + MethodGen method = context.editMethod(methods[i], ce + .getClassName()); + if (method.getName().equals("")) { + db(" " + method); + MethodRef methRef = new MethodRef(new ObjectType(ce + .getClassName()), new NameAndType(method.getName(), + method.getReturnType()), method.getReturnType(), + method.getArgumentTypes()); + worklist.add(methRef); + } + } + } + } + + /** + * Adds the static initializer for a given Type to the worklist. + */ + void addClinit(Type type) { + try { + ClassGen ce = context.editClass(type); + + Method[] methods = ce.getMethods(); + for (int i = 0; i < methods.length; i++) { + MethodGen clinit = context.editMethod(methods[i], ce + .getClassName()); + if (clinit.getName().equals("")) { + MethodRef methRef = new MethodRef(new ObjectType(ce + .getClassName()), new NameAndType(clinit.getName(), + clinit.getReturnType()), clinit.getReturnType(), + clinit.getArgumentTypes()); + + worklist.add(methRef); + context.release(clinit.getMethod(), ce.getClassName()); + break; + } + context.release(clinit.getMethod(), ce.getClassName()); + } + context.release(ce.getJavaClass()); + + } catch (ClassNotFoundException ex1) { + System.err.println("** Could not find class for " + type); + ex1.printStackTrace(System.err); + System.exit(1); + } + } + + /** + * Handles a virtual call. Determines all possible methods the call could + * resolve to. Adds the method whose declaring classes are live to the + * worklist. Blocks the rest on their declaring types. + */ + void doVirtual(MethodGen caller, MethodRef callee) { + // Figure out which methods the callee can resolve to. + Iterator resolvesToWith = hier.resolvesToWith(callee).iterator(); + + while (resolvesToWith.hasNext()) { + ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) resolvesToWith + .next(); + db(" resolves to " + rtw.method); + + // Add all possible non-abstract methods to the call graph. + // This way, when a blocked method becomes unblocked, it will + // still be in the call graph. + addCall(caller, rtw.method); + + Iterator rTypes = rtw.rTypes.iterator(); + boolean isLive = false; // Is one of the rTypes live? + while (rTypes.hasNext()) { + Type rType = (Type) rTypes.next(); + if (liveClasses.contains(rType)) { + isLive = true; + db(" Method " + rtw.method + " is live"); + worklist.add(rtw.method); + break; + } + } + + if (!isLive) { + // If none of the receiver types is live, then the method is + // blocked on all possible receiver types. + rTypes = rtw.rTypes.iterator(); + StringBuffer sb = new StringBuffer(); + while (rTypes.hasNext()) { + Type rType = (Type) rTypes.next(); + Set blockedMethods = (Set) blocked.get(rType); + if (blockedMethods == null) { + blockedMethods = new LinkedHashSet(); + blocked.put(rType, blockedMethods); + } + blockedMethods.add(rtw.method); + sb.append(rType.toString()); + if (rTypes.hasNext()) { + sb.append(','); + } + } + db(" Blocked " + rtw.method + " on " + sb); + } + } + } + + /** + * Makes note of one method calling another. This does not make the method + * live. + */ + void addCall(MethodGen callerMethod, MethodRef callee) { + // Just maintain the calls mapping + MethodRef caller = new MethodRef(new ObjectType(callerMethod + .getClassName()), new NameAndType(callerMethod.getName(), + callerMethod.getReturnType()), callerMethod.getReturnType(), + callerMethod.getArgumentTypes()); + + Set callees = (Set) this.calls.get(caller); + if (callees == null) { + callees = new LinkedHashSet(); + this.calls.put(caller, callees); + } + callees.add(callee); + } + + /** + * Marks a Type as being lives. It also unblocks any methods that + * were blocked on the type. + */ + void makeLive(Type type) { + if (this.liveClasses.contains(type)) { + return; + } + + // Make type live and unblock all methods blocked on it + db(" Making " + type + " live"); + liveClasses.add(type); + Set blockedMethods = (Set) blocked.remove(type); + if (blockedMethods != null) { + Iterator iter = blockedMethods.iterator(); + while (iter.hasNext()) { + MemberRef method = (MemberRef) iter.next(); + db(" Unblocking " + method); + worklist.add(method); + } + } + } + + /** + * Returns the methods (MemberRefs) to which a given method + * could resolve. Only live methods are taken into account. The methods are + * sorted such that overriding methods appear before overriden methods. + */ + public Set resolvesTo(MethodRef method) { + TreeSet resolvesTo = (TreeSet) this.resolvesTo.get(method); + + if (resolvesTo == null) { + resolvesTo = new TreeSet(new MemberRefComparator(context)); + this.resolvesTo.put(method, resolvesTo); + + Set liveMethods = this.liveMethods(); + Iterator rtws = hier.resolvesToWith(method).iterator(); + while (rtws.hasNext()) { + ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) rtws + .next(); + if (liveMethods.contains(rtw.method)) { + resolvesTo.add(rtw.method); + } + } + } + + // Return a clone so that the set may safely be modified + return ((Set) resolvesTo.clone()); + } + + /** + * Returns the methods (MemberRefs) to which a given method + * could resolve given that the receiver is in a certain set of types. Only + * live methods are taken into account. The methods are sorted such that + * overriding methods appear before overriden methods. + */ + public Set resolvesTo(MethodRef method, Set rTypes) { + if (rTypes.isEmpty()) { + return (resolvesTo(method)); + } + + // Since we're only dealing with a subset of types, don't bother + // with the caching stuff. + TreeSet resolvesTo = new TreeSet(new MemberRefComparator(context)); + + Set liveMethods = this.liveMethods(); + Iterator rtws = hier.resolvesToWith(method).iterator(); + while (rtws.hasNext()) { + ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) rtws + .next(); + if (liveMethods.contains(rtw.method)) { + HashSet clone = (HashSet) rtw.rTypes.clone(); + + clone.retainAll(rTypes); + if (!clone.isEmpty()) { + // Only keep method that have at least one possible + // receiver type in rTypes + resolvesTo.add(rtw.method); + } + } + } + + // Return a clone so that the set may safely be modified + return ((Set) resolvesTo.clone()); + } + + /** + * Returns the set of methods (MemberRefs) that the + * construction algorithm has deemed to be live. + */ + public Set liveMethods() { + // Not all of the methods in the calls mapping are necessarily + // live. So, we have to maintain a separate set. + return (this.liveMethods); + } + + /** + * Returns the root methods (MemberRefs) of the call graph. + */ + public Set roots() { + return (this.roots); + } + + /** + * Returns the set of classes (Types) that are instantiated in + * the program. + */ + public Set liveClasses() { + return (this.liveClasses); + } + + /** + * Prints a textual prepresentation of the CallGraph to a + * PrintWriter. + * + * @param out + * To where we print + * @param printLeaves + * If true, leaf methods (methods that do not call + * any other methods) are printed + */ + public void print(PrintWriter out, boolean printLeaves) { + + Iterator callers = calls.keySet().iterator(); + while (callers.hasNext()) { + MethodRef caller = (MethodRef) callers.next(); + + Iterator callees = ((Set) calls.get(caller)).iterator(); + + if (!printLeaves && !callees.hasNext()) { + continue; + } + + out.print(caller.declaringClass() + "." + caller.name() + + caller.getSignature()); + if (roots.contains(caller)) { + out.print(" (root)"); + } + out.println(""); + + while (callees.hasNext()) { + MethodRef callee = (MethodRef) callees.next(); + + // Only print live methods + if (!calls.containsKey(callee)) { + continue; + } + + out.println(" " + callee.declaringClass() + "." + + callee.name() + callee.getSignature()); + } + + out.println(""); + } + } + + /** + * Prints a summary of the call graph. Including the classes that are live + * and which methods are blocked. + */ + public void printSummary(PrintWriter out) { + out.println("Instantiated classes:"); + Iterator instantiated = this.liveClasses.iterator(); + while (instantiated.hasNext()) { + Type type = (Type) instantiated.next(); + out.println(" " + type.toString()); + } + + out.println("\nBlocked methods:"); + if (blocked != null) { + Iterator types = blocked.keySet().iterator(); + while (types.hasNext()) { + Type type = (Type) types.next(); + out.println(" " + type); + + Set set = (Set) blocked.get(type); + if (set != null) { + Iterator methods = set.iterator(); + while (methods.hasNext()) { + MethodRef method = (MethodRef) methods.next(); + out.println(" " + method); + } + } + } + } + + out.println("\nCall graph:"); + this.print(out, false); + } + +} + +/** + * CallVisitor examines the instructions in a method and notices what + * methods are called and which classes are created. + */ +class CallVisitor extends InstructionAdapter { + MethodGen caller; + + CallGraph cg; + + boolean firstSpecial; // Are we dealing with the first invokespecial? + + private static void db(String s) { + CallGraph.db(s); + } + + public CallVisitor(CallGraph cg) { + this.cg = cg; + } + + public void setCaller(MethodGen caller) { + this.caller = caller; + if (caller.getName().equals("")) { + this.firstSpecial = true; + } else { + this.firstSpecial = false; + } + } + + public void visit(Instruction inst, ConstantPoolGen cpg) { + if (inst instanceof InvokeInstruction) { + if (inst instanceof INVOKEVIRTUAL) { + visit_invokevirtual((InvokeInstruction) inst); + } else if (inst instanceof INVOKEINTERFACE) { + visit_invokeinterface((InvokeInstruction) inst); + } else if (inst instanceof INVOKESTATIC) { + visit_invokestatic((InvokeInstruction) inst); + } else if (inst instanceof INVOKESPECIAL) { + visit_invokespecial((InvokeInstruction) inst); + } + } else if (inst instanceof GETSTATIC) { + visit_getstatic((GETSTATIC) inst); + } else if (inst instanceof PUTSTATIC) { + visit_putstatic((PUTSTATIC) inst); + } else if (inst instanceof NEW) { + visit_new((NEW) inst); + } + } + + public void visit_invokevirtual(InvokeInstruction inst, ConstantPoolGen cpg) { + db("\n Visiting Call: " + inst); + + this.firstSpecial = false; + + // Call doVirtual to determine which methods this call may resolve + // to are live. + MethodRef callee = new MethodRef((ObjectType) inst + .getReferenceType(cpg), new NameAndType( + inst.getMethodName(cpg), inst.getReturnType(cpg)), inst + .getReturnType(cpg), inst.getArgumentTypes(cpg)); + cg.doVirtual(caller, callee); + } + + public void visit_invokeinterface(InvokeInstruction inst, + ConstantPoolGen cpg) { + db("\n Visiting Call: " + inst); + + this.firstSpecial = false; + + // Pretty much the same as invokevirtual + MethodRef callee = new MethodRef((ObjectType) inst + .getReferenceType(cpg), new NameAndType( + inst.getMethodName(cpg), inst.getReturnType(cpg)), inst + .getReturnType(cpg), inst.getArgumentTypes(cpg)); + cg.doVirtual(caller, callee); + } + + public void visit_invokestatic(InvokeInstruction inst, ConstantPoolGen cpg) { + db("\n Visiting call: " + inst); + + this.firstSpecial = false; + + // There's not a lot to do with static methods since there is no + // dynamic dispatch. + MethodRef callee = new MethodRef((ObjectType) inst + .getReferenceType(cpg), new NameAndType( + inst.getMethodName(cpg), inst.getReturnType(cpg)), inst + .getReturnType(cpg), inst.getArgumentTypes(cpg)); + cg.addCall(caller, callee); + cg.worklist.add(callee); + } + + public void visit_invokespecial(InvokeInstruction inst, ConstantPoolGen cpg) { + db("\n Visiting call: " + inst); + + // Recall that invokespecial is used to call constructors, private + // methods, and "super" methods. There is no dynamic dispatch for + // special methods. + MethodRef callee = new MethodRef((ObjectType) inst + .getReferenceType(cpg), new NameAndType( + inst.getMethodName(cpg), inst.getReturnType(cpg)), inst + .getReturnType(cpg), inst.getArgumentTypes(cpg)); + + MethodGen calleeMethod = null; + + try { + calleeMethod = cg.context.editMethod(callee); + + } catch (NoSuchMethodException ex1) { + System.err.println("** Couldn't find method: " + callee); + System.exit(1); + } + + if (calleeMethod.isSynchronized() || calleeMethod.isNative()) { + // Calls to synchronized and native methods are virtual + cg.doVirtual(caller, callee); + + } else { + // Calls to everything else (superclass methods, private + // methods, etc.) do not involve a dynamic dispatch and can be + // treated like a static method. + cg.addCall(caller, callee); + cg.worklist.add(callee); + } + + cg.context.release(calleeMethod.getMethod(), calleeMethod + .getClassName()); + } + + public void visit_getstatic(GETSTATIC inst, ConstantPoolGen cpg) { + // Referencing a static fields implies that its class's static + // initializer has been invoked. + db("\n Referencing static field " + inst); + cg.addClinit((ObjectType) inst.getReferenceType(cpg)); + } + + public void visit_putstatic(PUTSTATIC inst, ConstantPoolGen cpg) { + // Referencing a static field implies that its class's static + // initializer has been invoked. + db("\n Referencing static field " + inst); + cg.addClinit((ObjectType) inst.getReferenceType(cpg)); + } + + public void visit_new(NEW inst, ConstantPoolGen cpg) { + // The new instruction instantiates a type and thus makes it live. + cg.makeLive(inst.getLoadClassType(cpg)); + } +} + +/** + * Compares MemberRefs such that overriding methods are less than + * overridden methods. + */ +class MemberRefComparator implements Comparator { + // TypeComparator c; + + public MemberRefComparator(InlineContext context) { + // c = new TypeComparator(context); + } + + public int compare(Object o1, Object o2) { + Assert.isTrue(o1 instanceof MethodRef, o1 + " is not a MethodRef"); + Assert.isTrue(o2 instanceof MethodRef, o2 + " is not a MethodRef"); + + MethodRef ref1 = (MethodRef) o1; + MethodRef ref2 = (MethodRef) o2; + Type type1 = ref1.declaringClass(); + Type type2 = ref2.declaringClass(); + if (type1 instanceof ArrayType && type2 instanceof ArrayType) { + type1 = ((ArrayType) type1).getElementType();// TODO + type2 = ((ArrayType) type2).getElementType(); + } + + try { + if (type1 instanceof BasicType) { + if (type2 instanceof BasicType) { + return (type1 == type2 ? 1 : -1); + } else { + return -1; + } + } + if (type2 instanceof BasicType) { + return -1; + } + // Both must be ObjectTypes now + if (((ObjectType) type1).subclassOf((ObjectType) type2)) + return -1; + } catch (ClassNotFoundException e) { + // Ignore, for now. + } + + return 1; + } + + public boolean compareTo(Object other) { + return (other instanceof MemberRefComparator); + } +} diff --git a/src/edu/purdue/cs/bloat/inline/Inline.java b/src/edu/purdue/cs/bloat/inline/Inline.java new file mode 100644 index 0000000..2032eb0 --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/Inline.java @@ -0,0 +1,730 @@ +/* + * Class: Inline + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.inline; + +import java.util.*; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.editor.NameAndType; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Inlines the code of non-virtual method call sites. These sites include calls + * to static methods and certain uses of the invokespecial method. + * There are certain metrics that can be set to effect where and how inlining is + * performed. + */ +public class Inline { + public static boolean DEBUG = false; + + private int maxCodeSize; // Max number of instructions in method + + private int maxInlineSize; // Don't inline methods larger than this + + private int maxCallDepth; // Max of height of call stack + + private boolean inlineExceptions; // Inline methods that throw exceptions + + private InlineContext context; + + /** + * Size of the largest method that can be inlined + */ + public static int CALLEE_SIZE = 100000; + + private static void db(String s) { + if (DEBUG) { + System.out.println(s); + } + } + + /** + * Constructor. By default the first-level calls are only inlined one level + * deep, there is no max size on methods to inline, and methods that may + * throw exceptions are inlined. + * + * @param maxCodeSize + * The maximum number of instructions a method can grow to. + */ + public Inline(InlineContext context, int maxCodeSize) { + this.context = context; + this.maxCodeSize = maxCodeSize; + this.maxInlineSize = -1; + this.maxCallDepth = 1; + this.inlineExceptions = true; + } + + /** + * Sets the maximum of size of a method that will be inlined. No method + * larger than this will be inlined. + */ + public void setMaxInlineSize(int maxInlineSize) { + this.maxInlineSize = maxInlineSize; + } + + /** + * Sets the maximum number of nested calls we inline. + */ + public void setMaxCallDepth(int maxCallDepth) { + this.maxCallDepth = maxCallDepth; + } + + /** + * Sets whether or not methods that may throw exceptions (that is, have a + * non-empty "throws" declaration) are inlined. + */ + public void setInlineExceptions(boolean inlineExceptions) { + this.inlineExceptions = inlineExceptions; + } + + /** + * Scans a method and inlines non-virtual method calls according to this + * Inline's metrics. + */ + public void inline(MethodGen method) { + ConstantPoolGen cpg = method.getConstantPool(); + // Go through the method and look for calls to inline + StackHeightCounter stackHeight = new StackHeightCounter(method); + InstructionList code = method.getInstructionList(); + FlowGraph.buildBLOATInstructionList(method); + boolean firstCall = true; + Iterator iter = code.iterator(); + while (iter.hasNext()) { + InstructionHandle ih = (InstructionHandle) iter.next(); + if (FlowGraph.label(ih)) { + // stackHeight.handle(ih); + // All instructions get handled later + db(" " + ih + "." + stackHeight.height() + ") " + ih + + (FlowGraph.label(ih) ? " (starts block)" : "")); + } + + Instruction inst = ih.getInstruction(); + if (inst instanceof INVOKESTATIC || inst instanceof INVOKESPECIAL) { + InvokeInstruction iI = (InvokeInstruction) inst; + MethodRef callee = new MethodRef((ObjectType) iI + .getReferenceType(cpg), new NameAndType(iI + .getMethodName(cpg), iI.getReturnType(cpg)), iI + .getReturnType(cpg), iI.getArgumentTypes(cpg)); + Stack callStack = new Stack(); + + callStack.add(new MethodRef(new ObjectType(method + .getClassName()), new NameAndType(method.getName(), + method.getReturnType()), method.getReturnType(), method + .getArgumentTypes())); + + db(" Call: " + inst); + + stackHeight.handle(ih); + int expectedHeight = stackHeight.height(); + stackHeight.unhandle(ih); + + InstructionHandle jh = ih; + ih = inline(method, callee, ih, callStack, stackHeight, + firstCall); + + if (jh == ih) { + // Call was not inlined, add it to the stack + stackHeight.handle(ih); + db(" " + ih + "." + stackHeight.height() + ") " + inst); + } + + int newHeight = stackHeight.height(); + // If an exception is thrown as the last thing in a method + // newHeight will equal 0. Let's let this one slide. + Assert.isTrue(newHeight == 0 || newHeight == expectedHeight, + "Inlining did not get the stack heights right: " + + "Expected " + expectedHeight + ", got " + + newHeight); + + } else { + stackHeight.handle(ih); + db(" " + ih + "." + stackHeight.height() + ") " + inst); + } + + if (inst instanceof InvokeInstruction) { + firstCall = false; + } + + } + + // method.setCode(code); WE JUST MODIFIED IT SO CHANGES ARE COMMITED + + if (DEBUG) { + stackHeight = new StackHeightCounter(method); + db("\nNew Code for " + method.getClassName() + "." + + method.getName() + method.getReturnType()); + code = method.getInstructionList(); + Iterator iterator = method.getInstructionList().iterator(); + InstructionHandle j = null; + while (iterator.hasNext()) { + j = (InstructionHandle) iterator.next(); + if (FlowGraph.label(j)) { + + // stackHeight.handle(j); + + CodeExceptionGen[] tryCatches = method + .getExceptionHandlers(); + for (int i = 0; i < tryCatches.length; i++) { + CodeExceptionGen tryCatch = tryCatches[i]; + if (tryCatch.getStartPC().equals(j)) { + System.out.println(" Begin protected region"); + } + + if (tryCatch.getEndPC().equals(j)) { + System.out.println(" End protected region"); + } + + // A Label can both end a protected region and begin + // catch + // block + + if (tryCatch.getHandlerPC().equals(j)) { + System.out.println(" Catch " + + tryCatch.getCatchType()); + } + + } + + } + stackHeight.handle(j); + System.out.println(" " + j + "." + stackHeight.height() + ") " + + j.getInstruction() + + (FlowGraph.label(j) ? " (starts block)" : "")); + + } + + // Print try-catch information + CodeExceptionGen[] tryCatches = method.getExceptionHandlers(); + System.out.println("Exception information:"); + for (int i = 0; i < tryCatches.length; i++) { + System.out.println(" " + tryCatches[i]); + } + System.out.println(""); + } + + } + + /** + * Helper method that does most of the work. By calling this method + * recursively, we can inline more than one call deep. + * + * @param caller + * The original caller that got all of this started. Into this + * method we insert the code. + * @param callee + * The method to be inlined + * @param index + * Where in caller we insert inlined code + * @param callStack + * A stack of MemberRefs that represent the inlined + * methods that call other methods. It is used to detect + * recursion. + * + * @return The index into the caller's code array of the instruction + * following the last inlinined instruction. Start looking here + * after inline returns. + */ + // isRecursiveCount added so I can be sure of getting a unique local + // variable name + private InstructionHandle inline(MethodGen caller, MethodRef callee, + InstructionHandle index, Stack callStack, + StackHeightCounter stackHeight, boolean firstCall) { + + Instruction newInst = null; + int LOCALVARINDEX = caller.getMaxLocals() - 1; + + // Do we ignore the method being inlined? + if (context.ignoreMethod(callee)) { + db(" Can't inline " + callee + ": it's ignored"); + index = index.getNext(); + return (index); + } + + // Can we inline this method + if (callStack.size() > maxCallDepth) { + db(" Can't inline " + callee + ": max call depth (" + maxCallDepth + + ") reached"); + index = index.getNext(); + return (index); + + } else if (callStack.contains(callee)) { + db(" Can't inline recursive call to " + callee); + index = index.getNext(); + return (index); + } + + // Make sure we're not inlining the static-ized version of a + // method in the call stack. + String name = callee.name(); + // Is this stuff that BCEL would do? or has a BLOAT optimization done it + int b = name.indexOf("$$BLOAT"); + if (b != -1) { + name = name.substring(0, b); + + // Get rid of first parameter + Type[] oldParams = callee.paramTypes(); + Type[] newParams = new Type[oldParams.length - 1]; + for (int p = 1; p < oldParams.length; p++) { + newParams[p - 1] = oldParams[p]; + } + + MethodRef unBloated = new MethodRef(callee.declaringClass(), + new NameAndType(name, callee.returnType()), callee + .returnType(), newParams); + + if (callStack.contains(unBloated)) { + db(" Can't inline recursive call to " + callee); + index = index.getNext(); + return (index); + } + } + + InstructionList code = caller.getInstructionList(); + if (code.size() > maxCodeSize) { + db(" Can't inline " + callee + ": max code size (" + maxCodeSize + + ") reached"); + index = index.getNext(); + return (index); + } + + MethodGen calleeMethod = null; + try { + calleeMethod = context.editMethod(callee); + + } catch (NoSuchMethodException ex) { + System.err.println("Couldn't find method " + callee); + ex.printStackTrace(System.err); + System.exit(1); + } + + if (calleeMethod.isNative()) { + db(" Can't inline " + callee + ": it's a native method"); + index = index.getNext(); + return (index); + } + + if (calleeMethod.isSynchronized()) { + db(" Can't inline " + callee + ": it's synchronized"); + index = index.getNext(); + return (index); + } + + if (!inlineExceptions && calleeMethod.getExceptions().length > 0) { + db(" Can't inline " + callee + ": it may throw an exception"); + index = index.getNext(); + return (index); + } + + if (calleeMethod.getInstructionList().size() > maxInlineSize) { + db(" Can't inline " + callee + ": it's too big"); + index = index.getNext(); + return (index); + } + + // Methods that catch exceptions are problematic. When an + // exception is thrown, it clears the stack. Ordinarily this + // isn't a problem. However, now the stack of the caller is + // cleared in addition to the stack of the callee. This is bad. + // The callee might catch the exception and deal with it. + // However, the stack has still been cleared. This really messes + // things up for the code that appears after the inlined method. + // So, if a method catches an exception, we can only inline it if + // the stack contains nothing but the parameters to the call. + if (calleeMethod.getExceptionHandlers().length > 0) { + int size = 0; + Type[] args = callee.paramTypes(); + for (int i = 0; i < args.length; i++) { + size = size + args[i].getSize(); + } + if (stackHeight.height() > size) { + db(" Can't inline " + callee + + ": It catches an exception and there's stuff on the " + + "stack"); + index = index.getNext(); + return (index); + } + } + + // If the callee method catches any of the same exceptions as the + // protected region that we are currently in, then we can't inline + // the method. + CodeExceptionGen[] tryCatches0 = calleeMethod.getExceptionHandlers(); + for (int i = 0; i < tryCatches0.length; i++) { + CodeExceptionGen tc1 = tryCatches0[i]; + Iterator iter = stackHeight.tryCatches().iterator(); + while (iter.hasNext()) { + CodeExceptionGen tc2 = (CodeExceptionGen) iter.next(); + Type t1 = tc1.getCatchType(); + Type t2 = tc2.getCatchType(); + if (t1 != null && t2 != null && t1.equals(t2)) { + db(" Can't inline " + callee + + ": It catches the same type " + + tc1.getCatchType().getClassName() + + " as the current protected region"); + index = index.getNext(); + return (index); + } + } + } + + // If the caller is a constructor and this is the first + // invokespecial we've seen in this callee method, we can inline + // calls to the constructors of superclasses and other + // constructors in this method. So, if this IS the first call in + // a method which IS a constructor, we can inline it. + if (calleeMethod.getName().equals("") + && (!firstCall || !caller.getName().equals(""))) { + + db(" Can't inline " + callee + ": It calls a normal constructor"); + index = index.getNext(); + return (index); + } + + // Local variables are problematic. We cannot simply map the + // callee's variables to new variables in the caller because we + // cannot precisely determine the width of the variable the first + // time we see it. (For instance, Nate's generated code might use + // a local variable as a non-wide initially and then use it as a + // wide later.) + + // Okay, we going to inline. Remove the calling instruction. + Instruction call = index.getInstruction(); + try { + code.delete(index); + } catch (TargetLostException tle) { + // TargetLost? Oh well, go on with inlining + } + index = index.getPrev(); + db(" Removing call: " + call); + Assert.isTrue(call instanceof INVOKESTATIC + || call instanceof INVOKESPECIAL, + "Removing the wrong call instruction:" + call); + callStack.push(callee); + + db(" Inlining call (" + callStack.size() + ") to " + + callee.declaringClass() + "." + callee.name() + callee.type()); + context.getInlineStats().noteInlined(); + + // ALL RIGHT, CREATE LOCALVARAIBLES FOR EACH OF THE LOCALS IN THE CALLEE + // The index should be LOCALVARINDEX + the index set in the calle code + // These will be added where they are params or garden variety locals. + // indexes shouldn't be a problem. + LocalVariableGen[] locals = calleeMethod.getLocalVariables(); + for (int i = 0; i < locals.length; i++) { + caller.addLocalVariable("inline" + calleeMethod.getName() + i, + locals[i].getType(), index, index.getNext()); + } + + // First we have to pop the arguments off the stack and store them + // into the local variables. Remember that wide types occupy two + // local variables. + + // Mapper mapper = new Mapper(caller); + // Type[] paramTypes = callee.paramTypes(); + + // IS THERE ANY REASON TO GO BACKWARDS? + for (int i = locals.length - 1; i >= 0; i--) { + // Map the local variables containing the arguments to new + // local variables. + Type paramType = locals[i].getType(); + + if (paramType == null) { + continue; + } + + db(" Param " + i + ": of type " + paramType); + + // LocalVariable newVar = mapper.map(param, paramType); + // I've already created the new LocalVariable + + if (paramType instanceof ReferenceType) { + newInst = new ASTORE(locals[i].getIndex()); + } else { + if (paramType.equals(Type.BOOLEAN) + || paramType.equals(Type.BYTE) + || paramType.equals(Type.CHAR) + || paramType.equals(Type.SHORT) + || paramType.equals(Type.INT)) { + newInst = new ISTORE(locals[i].getIndex()); + } else if (paramType.equals(Type.DOUBLE)) { + newInst = new DSTORE(locals[i].getIndex()); + } else if (paramType.equals(Type.LONG)) { + newInst = new LSTORE(locals[i].getIndex()); + } else if (paramType.equals(Type.FLOAT)) { + newInst = new FSTORE(locals[i].getIndex()); + } else { + throw new IllegalArgumentException("What's a " + paramType + + "doing as a method " + "parameter"); + } + + } + + index = caller.getInstructionList().append(index, newInst); + stackHeight.handle(index); + db(" " + index + "." + stackHeight.height() + "> " + newInst); + } + // SO ALL THE LOCALS ARE SETUP + + // Before we mess with the code, we have to patch up the try-catch + // information from the inlined method to the caller method. + CodeExceptionGen[] tryCatches = calleeMethod.getExceptionHandlers(); + for (int i = 0; i < tryCatches.length; i++) { + CodeExceptionGen tryCatch = tryCatches[i]; + + // INSTRUCTIONHANDLES STAY INTACT WHEN A INSTRUCTIONLIST IS + // ADDED + // SO WE DON"T NEED TO TRANSLATE LABELS AS BLOAT DID + InstructionHandle start = tryCatch.getStartPC(); + InstructionHandle end = tryCatch.getEndPC(); + InstructionHandle handler = tryCatch.getHandlerPC(); + + caller.addExceptionHandler(start, end, handler, tryCatch + .getCatchType()); + } + + // Go through the code in the callee method and inline it. Handle + // any calls by making a recursive call to this method. Copy each + // instruction to the method in which it is being inlined. Along + // the way convert references to local variables to their mapped + // values. Also remove return instructions. Replace them with + // loads as necessary. + + FlowGraph.buildBLOATInstructionList(calleeMethod); + InstructionList inlineCode = calleeMethod.getInstructionList().copy(); + // I want to play with the instructionlist and then add it to the caller + // code in one go which saves me messing with mapping instructionHandles + + // We don't want to introduce a new end label because it confuses + // BLOAT during CFG construction. We designate the end label as + // starting a new block in hopes that it will solve problems with + // CFG construction. + InstructionHandle last = inlineCode.append(new NOP()); + InstructionHandle endLabel; + if (FlowGraph.label(last)) { + endLabel = last; + } else { + FlowGraph.setLabel(last); + endLabel = last; + } + // endLabel.setStartsBlock(true); + + firstCall = true; + Iterator iter = inlineCode.iterator(); + while (iter.hasNext()) { + InstructionHandle instHandle = (InstructionHandle) iter.next(); + Instruction inst = instHandle.getInstruction(); + + if (FlowGraph.label(instHandle)) { + // code.add(++index, newLabel); + // stackHeight.handle(newLabel); + db(" " + + index + + "." + + stackHeight.height() + + "> " + + inst + + (FlowGraph.label(instHandle) ? " (starts block)" : "")); + } + + // Assert.isTrue(o instanceof Instruction, "What is a " + o + + // " doing in the instruction stream?"); + + // int opcode = inst.opcodeClass(); + + if (inst instanceof LocalVariableInstruction) { + // Map local variable in the callee method to local + // variables in the caller method. + // LocalVariable local = mapper.map((LocalVariable) operand, + // (inst.category() == 2 ? true : false)); + // operand = local; + LocalVariableInstruction lvi = (LocalVariableInstruction) inst; + lvi.setIndex(lvi.getIndex() + LOCALVARINDEX); + + // } else if(inst instanceof Label) { + // Map labels in the callee method to labels in the caller + // method. + // Label label = mapper.map((Label) operand); + // operand = label; + // HOPEFULLY THE InstructionHandles STAY INTACT + + } else if (inst instanceof Select) { + // We have to patch up the Labels involved with the Switch + // Switch oldSwitch = (Switch) operand; + + // Label newDefault = mapper.map(oldSwitch.defaultTarget()); + + // Label[] oldTargets = oldSwitch.targets(); + // Label[] newTargets = new Label[oldTargets.length]; + // for(int i = 0; i < newTargets.length; i++) { + // Label newTarget = mapper.map(oldTargets[i]); + // newTargets[i] = newTarget; + // } + + // operand = new Switch(newDefault, newTargets, + // oldSwitch.values()); + // HOPEFULLY THE InstructionHandles STAY INTACT + } + + if (inst instanceof ReturnInstruction) { + // Insert a jump to the end of the inlined method. Any + // return value will be on top of the stack. This is where + // we want it. + newInst = new GOTO(endLabel); + instHandle.setInstruction(newInst); + // code.append(index, newInst); + index = index.getNext(); + stackHeight.handle(instHandle); + db(" " + index + "." + stackHeight.height() + "> " + newInst); + + } else if (inst instanceof INVOKESTATIC + || inst instanceof INVOKESPECIAL) { + // Make a recursive call. Note that this must be done after + // we add the call instruction above. But we only want to + // visit the instruction with the stackHeight if the call was + // not inlined. + // newInst = new Instruction(opcode, operand); + // code.add(++index, newInst); + InvokeInstruction iI = (InvokeInstruction) inst; + + stackHeight.handle(instHandle); + int expectedHeight = stackHeight.height(); + stackHeight.unhandle(instHandle); + ConstantPoolGen cpg = calleeMethod.getConstantPool(); + MethodRef nestedCall = new MethodRef((ObjectType) iI + .getReferenceType(cpg), new NameAndType(iI + .getMethodName(cpg), iI.getReturnType(cpg)), iI + .getReturnType(cpg), iI.getArgumentTypes(cpg)); + InstructionHandle oldIndex = instHandle; + instHandle = inline(caller, nestedCall, instHandle, callStack, + stackHeight, firstCall); + + if (instHandle == oldIndex) { + stackHeight.handle(instHandle); + db(" " + instHandle + "." + stackHeight.height() + "> " + + inst); + } + + int newHeight = stackHeight.height(); + Assert.isTrue(newHeight == 0 || newHeight == expectedHeight, + "Inlining did not get the stack heights right: " + + "Expected " + expectedHeight + ", got " + + newHeight); + + } else { + // Add the instruction + // newInst = new Instruction(opcode, operand); + // code.add(++index, newInst); + stackHeight.handle(instHandle); + db(" " + index + "." + stackHeight.height() + "> " + newInst); + } + + // We want to do this after we've made any recursive calls to + // inline. + if (inst instanceof InvokeInstruction) { + firstCall = false; + } + + } + + code.append(index, inlineCode); + index = endLabel.getNext(); + // add the whole of the inline method code now that it has been modified + + // if(addEndLabel) { + // Done inlining. Add end label. + // code.add(++index, endLabel); + // stackHeight.handle(endLabel); + // db(" " + index + "." + stackHeight.height() + "> " + endLabel + + // (endLabel.startsBlock() ? " (starts block)" : "")); + // } + + // caller.setDirty(true); + callStack.pop(); + return (index); + + } +} + +/** + * Utility class for mapping local variables and labels. Note that when mapping + * local variables we have to be careful. We can't assume that a variable will + * retain its "wideness" throughout the method. I learned this one the hard way. + * So, we have to keep a constant difference between the mapped variables. + */ +/* + * class Mapper { private Map varsMap; // Maps local variables //private Map + * labelsMap; // Maps labels private MethodGen method; // Method into which + * things are mapped //private int offset; // Start numbering new locals here + * //adding locals to method will do this for us. + * + * private static void db(String s) { if(Inline.DEBUG) System.out.println(s); } + */ + +/** + * Constructor. + */ +/* + * public Mapper(MethodGen method) { this.method = method; varsMap = new + * HashMap(); labelsMap = new LinkedHashMap(); offset = method.maxLocals() + 1; } + * + * public Label map(Label label) { Label newLabel = (Label) + * labelsMap.get(label); if(newLabel == null) { newLabel = + * this.method.newLabel(); newLabel.setStartsBlock(label.startsBlock()); + * labelsMap.put(label, newLabel); db(" " + label + " -> " + newLabel + + * (newLabel.startsBlock() ? " (starts block)" : "")); } return(newLabel); } + * + * public LocalVariableGen map(LocalVariableGen var, Type type) { + * LocalVariableGen newVar = (LocalVariableGen) varsMap.get(var); if(newVar == + * null) { newVar = this.method.localAt(var.index() + offset); // newVar = + * this.method.newLocal(type); varsMap.put(var, newVar); db(" " + var + " (" + + * var.index() + ") -> " + newVar + "(" + var.index() + "+" + offset + ")" + + * (type.isWide() ? " (" + type + ")" : "")); } return(newVar); } + * + * public LocalVariable map(LocalVariable var, boolean isWide) { LocalVariable + * newVar = (LocalVariable) varsMap.get(var); if(newVar == null) { newVar = + * this.method.localAt(var.index() + offset); // newVar = + * this.method.newLocal(isWide); varsMap.put(var, newVar); db(" " + var + " (" + + * var.index() + ") -> " + newVar + "(" + var.index() + "+" + offset + ")" + + * (isWide ? " (wide)" : "")); } return(newVar); } } + */ + diff --git a/src/edu/purdue/cs/bloat/inline/InlineContext.java b/src/edu/purdue/cs/bloat/inline/InlineContext.java new file mode 100644 index 0000000..3a3e27c --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/InlineContext.java @@ -0,0 +1,122 @@ +/* + * Class: InlineContext + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.inline; + +import java.util.*; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; + +/** + * An InlineContext gives access to the CallGraph for the + * program whose classes are being operated on by BLOAT. + */ +public interface InlineContext extends EditorContext { + + /** + * Returns the call graph for the program. + */ + public CallGraph getCallGraph(); + + /** + * Sets the root methods for this InlineContext. + * + * @param roots + * The root methods (MemberRefs) of the program + * @throws IllegalStateException + * Call graph has already been created with different roots. + */ + public void setRootMethods(Set roots); + + /** + * Returns an InlineStats object for getting statistics about + * inlining. + */ + public InlineStats getInlineStats(); + + /** + * Notes that all classes, methods, and fields in a package should be + * "ignored" by inlining. That is, methods won't be inlined, classes won't + * be involved in specialization, etc. Note that it is exceptable to just + * add a prefix of a package name. For instance, adding "java" will ignore + * java.lang.Object, java.io.File, etc. + */ + public void addIgnorePackage(String name); + + /** + * Notes that a class should be ignored by inlining. That is, none of its + * methods will be inlined and it won't be involved in specialization. + */ + public void addIgnoreClass(Type type); + + /** + * Notes that a method should be ignored by inlining. That is, it will not + * be inlined. + */ + public void addIgnoreMethod(MethodRef method); + + /** + * Notes that a field should be ignored by inlining. + */ + public void addIgnoreField(MemberRef field); + + /** + * Returns true if a class should be ignored by inlining. + */ + public boolean ignoreClass(Type type); + + /** + * Returns true if a method should be ignored by inlining. + */ + public boolean ignoreMethod(MethodRef method); + + /** + * Returns true if a field should be ignored by inlining. + */ + public boolean ignoreField(MemberRef field); + + /** + * Sets whether or not we ignore all system classes. + */ + public void setIgnoreSystem(boolean ignoreSystem); +} diff --git a/src/edu/purdue/cs/bloat/inline/InlineStats.java b/src/edu/purdue/cs/bloat/inline/InlineStats.java new file mode 100644 index 0000000..bff894f --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/InlineStats.java @@ -0,0 +1,152 @@ +/* + * Class: InlineStats + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.inline; + +import java.io.*; +import java.util.*; + +/** + * This class is used to gather statistics about inlining. Examples of such + * statistics are the number of call sites virtual methods resolve to and the + * number of live classes and methods. + */ +public class InlineStats { + private String configName; // Name of configuration + + private Map morphicity; // Maps morphic number to count + + private int nLiveClasses; // Number of live classes + + private int nLiveMethods; // Number of live methods + + private int nNoPreexist; // Number of non-preexistent calls + + private int nInlined; // Number of methods inlined + + /** + * Constructor. + * + * @param configName + * A string describing the configuration being run + */ + public InlineStats() { + this.configName = "Inlining stats"; + this.morphicity = new TreeMap(); + this.nLiveClasses = 0; + this.nLiveMethods = 0; + this.nNoPreexist = 0; + this.nInlined = 0; + } + + /** + * Sets the configuration name for this InlineStats. + */ + public void setConfigName(String configName) { + this.configName = configName; + } + + /** + * Maintains a count of the number of methods call sites resolve to. May + * give an idea as to how "dynamic" a program is. + */ + public void noteMorphicity(int morphicity) { + Integer r = new Integer(morphicity); + Integer count = (Integer) this.morphicity.get(r); + if (count == null) { + this.morphicity.put(r, new Integer(1)); + + } else { + this.morphicity.put(r, new Integer(count.intValue() + 1)); + } + } + + /** + * Notes that a call site's receiver is not preexistent. + */ + public void noteNoPreexist() { + nNoPreexist++; + } + + /** + * Notes that a method was inlined + */ + public void noteInlined() { + this.nInlined++; + } + + /** + * Notes the number of live methods. + */ + public void noteLiveMethods(int nLiveMethods) { + this.nLiveMethods = nLiveMethods; + } + + /** + * Notes the number of live classes. + */ + public void noteLiveClasses(int nLiveClasses) { + this.nLiveClasses = nLiveClasses; + } + + /** + * Print a summary of the statistics to a PrintWriter. + */ + public void printSummary(PrintWriter pw) { + pw.println("Statistics for " + this.configName + " (" + new Date() + + ")"); + pw.println(" Number of live classes: " + this.nLiveClasses); + pw.println(" Number of live methods: " + this.nLiveMethods); + pw.println(" Call site morphism:"); + + Iterator morphs = this.morphicity.keySet().iterator(); + int total = 0; + while (morphs.hasNext()) { + Integer morph = (Integer) morphs.next(); + Integer count = (Integer) this.morphicity.get(morph); + total += count.intValue(); + pw.println(" " + morph + "\t" + count); + } + pw.println(" Total number of call sites: " + total); + pw.println(" Number of non-preexistent call sites: " + nNoPreexist); + pw.println(" Number of inlined methods: " + nInlined); + + } + +} diff --git a/src/edu/purdue/cs/bloat/inline/Makefile b/src/edu/purdue/cs/bloat/inline/Makefile new file mode 100644 index 0000000..758f09a --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/Makefile @@ -0,0 +1,30 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + CallGraph.class\ + Inline.class\ + InlineContext.class\ + InlineStats.class\ + InstructionStack.class\ + StackHeightCounter.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/inline/StackHeightCounter.java b/src/edu/purdue/cs/bloat/inline/StackHeightCounter.java new file mode 100644 index 0000000..54bc39b --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/StackHeightCounter.java @@ -0,0 +1,246 @@ +/* + * Class: StackHeightCounter + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.inline; + +import java.util.*; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.editor.InstructionAdapter; + +/** + * Used to keep track of the height of the stack. As instructions are visited, + * the height of the stack is adjusted accordingly. + */ +public class StackHeightCounter extends InstructionAdapter { + public static boolean DEBUG = false; + + private int height; // Current stack height + + private LinkedHashMap labelHeights; // Maps labels to their heights as + + // Integers + + private MethodGen method; // Method whose height we're computing + + Set tryCatches; // TryCatches active at current instruction + + private static void db(String s) { + if (DEBUG) + System.out.println(s); + } + + /** + * Constructor. + * + * @param height + * Initial height of stack + */ + public StackHeightCounter(MethodGen method) { + this.method = method; + this.height = 0; + this.labelHeights = new LinkedHashMap(); + this.tryCatches = new LinkedHashSet(); + } + + /** + * Returns the current height of the stack. + */ + public int height() { + return (this.height); + } + + /** + * Handles a Label. Special provisions must be made for labels that catch + * exceptions. + */ + // public void handle(Label label) { + // } + // EVERYTHING IS A INSTRUCTION NOW + /** + * Handles an instruction. Special provisions must be made to handle jumps, + * switches, throws, and returns. + */ + public void handle(InstructionHandle ih) { + if (FlowGraph.label(ih)) { + Integer labelHeight = (Integer) labelHeights.get(ih); + if (labelHeight != null) { + height = labelHeight.intValue(); + } + + // If this label begins an exception handler, then start it off + // with a new stack with one element (the exception object) on + // it. + CodeExceptionGen[] tryCatches = method.getExceptionHandlers(); + for (int i = 0; i < tryCatches.length; i++) { + if (tryCatches[i].getHandlerPC().equals(ih)) { + FlowGraph.setLabel(ih); + height = 1; + break; + } + + if (tryCatches[i].getStartPC().equals(ih)) { + // If this block starts a protected region make note of + // the + // TryCatch block + this.tryCatches.add(tryCatches[i]); + } + + if (tryCatches[i].getEndPC().equals(ih)) { + // If this block ends a protected region, remove it from + // the + // tryCatches list + this.tryCatches.remove(tryCatches[i]); + } + } + }// ends the bit where we handle Labels but because a labels are + // Instructions also.... + Instruction inst = ih.getInstruction(); + // inst.visit(this); + // Hopefully this avoids that annoying switch statement and all + // those visits + int delta = 0; + if (inst instanceof StackConsumer) + delta = delta + - ((StackConsumer) inst).consumeStack(method + .getConstantPool()); + if ((inst instanceof StackProducer))// &&!(inst instanceof + // JsrInstruction)) + delta = delta + - ((StackProducer) inst).produceStack(method + .getConstantPool()); + this.height = this.height + delta; + + if (inst instanceof IfInstruction || inst instanceof GotoInstruction) { + BranchInstruction bI = (BranchInstruction) inst; + InstructionHandle target = bI.getTarget(); + FlowGraph.setLabel(target); + Integer targetHeight = (Integer) labelHeights.get(target); + if (targetHeight != null) { + if (targetHeight.intValue() != height) { + // Make sure stack heights match + db("Stack height mismatch (" + targetHeight.intValue() + + " != " + height + ") at " + inst); + } + + } else { + labelHeights.put(target, new Integer(height)); + } + + } else if (inst instanceof Select) { + // Propagate height to all targets + Select sw = (Select) inst; + + InstructionHandle[] targets = sw.getTargets(); + for (int t = 0; t < targets.length; t++) { + InstructionHandle target = targets[t]; + FlowGraph.setLabel(target); + Integer targetHeight = (Integer) labelHeights.get(target); + if (targetHeight != null) { + if (targetHeight.intValue() != height) { + // Make sure stack heights match + db("Stack height mismatch (" + targetHeight.intValue() + + " != " + height + ") at " + inst); + } + } else { + labelHeights.put(target, new Integer(height)); + } + } + + } else if (inst instanceof JsrInstruction) { + // We have to account for the return address being pushed on the + // stack. Let's ignore the fact that someday in the future + // subroutines may push stuff on the stack. M'kay? + InstructionHandle subroutine = ((JsrInstruction) inst).getTarget(); + FlowGraph.setLabel(subroutine); + Integer subHeight = (Integer) labelHeights.get(subroutine); + if (subHeight != null) { + if (subHeight.intValue() != height + 1) { + db("Stack height mismatch at subroutine (" + + subHeight.intValue() + " != " + (height + 1) + + ") at " + inst); + } + + } else { + labelHeights.put(subroutine, new Integer(height + 1)); + } + + } else if (inst instanceof ExceptionThrower + || inst instanceof ReturnInstruction) { + // Clear the stack + height = 0; + } + } + + /** + * Simulates the effect of "backing up" over an instruction. + */ + public void unhandle(InstructionHandle inst) { + // Temporarily negate the stack height, perform the normal handle, + // and then negate the stack height again. + this.height = -this.height; + this.handle(inst); + this.height = -this.height; + } + + /** + * Returns the set of TryCatch objects for the protected region + * that the current instruction may be in. + */ + public Set tryCatches() { + return (this.tryCatches); + } + + /* + * public void visit_lushr(Instruction inst) { // Yes, it's only -1. The + * long and the int index are popped off // and the shifted value is pushed. + * Net loss of 1. height -= 1; } + * + * public void visit_jsr(Instruction inst) { // Even though the jsr + * instruction itself pushes the return // address onto the stack, we don't + * want to account for that // here. It is already taken care of in the + * handle method. This // way the label following the jsr (the return site) + * will have the // stack height it had before the call. Once again, we do + * not // account for the possibility of the jsr modifying the height of // + * the stack. height += 0; } + * + */ +} diff --git a/src/edu/purdue/cs/bloat/inline/package.html b/src/edu/purdue/cs/bloat/inline/package.html new file mode 100644 index 0000000..f9fa7b4 --- /dev/null +++ b/src/edu/purdue/cs/bloat/inline/package.html @@ -0,0 +1,19 @@ + + + +

Classes for performing method inlining. The first step in method +ininling is to create the call graph for a Java "program". Rapid Type +Analysis [Bacon 1997] is used to keep the size of the call graph +managable. RTA uses the class hierarchy to determine all possible +methods that may be invoked at a given call site. Methods are only +considered to be live if their class is instantiated somewhere in the +program.

+ +

Call sites are specialized using the resolves-to information +supplied by the call graph. Each virtual method call is transformed +into a "switch" statement on the possible receiver types. The +receiver is cast to a given type and a static method is invoked in +place of the virtual method.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/optimize/CodeGen.java b/src/edu/purdue/cs/bloat/optimize/CodeGen.java new file mode 100644 index 0000000..fc59b86 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/CodeGen.java @@ -0,0 +1,126 @@ +/* + * Class: CodeGen + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +import java.text.DateFormat; +import java.util.Date; +import edu.purdue.cs.bloat.codegen.RegisterAllocator; + +import edu.purdue.cs.bloat.codegen.CodeGenerator; + +/** + * CodeGen generates the code after the other optimizations have been performed. + * It's an Optimization itself, so you have to make sure it gets put in the list + * of optimizations to perform. + *

+ * + * CodeGen will print debugging information if the system property + * edu.purdue.cs.bloat.DEBUG is set to true. + * + * @author Chris Bennetts + * + */ +public class CodeGen implements Optimization { + private RegisterAllocator alloc = null; + + private static DateFormat dateFormat = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.LONG); + + private final boolean DEBUG; + + public CodeGen(RegisterAllocator alloc) { + DEBUG = "true".equals(System.getProperty("edu.purdue.cs.bloat.DEBUG")); + this.alloc = alloc; + } + + public void transform(MethodState state) { + // Liveness and RegisterAllocation take place in CodeGenerator + CodeGenerator codegen = new CodeGenerator(state); + + // Register allocation must occur before replacePhis(). + printTraceMessage(" Register allocation"); + + codegen.replacePhis(); + printPostPhisMessage(state); + + codegen.simplifyControlFlow(); + + codegen.allocateReturnAddresses(alloc); + printPostAllocationMessage(state); + codegen.generateCode(); + } + + public String traceMessage(String dateString) { + + return " Code Generation: " + dateString; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + private void printTraceMessage(String root) { + if (true) + return; + + String dateString = dateFormat.format(new Date()); + System.out.println(root + ": " + dateString); + } + + private void printPostPhisMessage(MethodState state) { + if (DEBUG) { + System.out.println("After fixing Phis------------------------"); + state.controlFlowGraph().print(System.out); + System.out.println("End print--------------------------------"); + } + } + + private void printPostAllocationMessage(MethodState state) { + if (DEBUG) { + System.out.println("After removing empty blocks--------------"); + state.controlFlowGraph().print(System.out); + System.out.println("End print--------------------------------"); + } + } +} diff --git a/src/edu/purdue/cs/bloat/optimize/ConstantPoolSizePrinter.java b/src/edu/purdue/cs/bloat/optimize/ConstantPoolSizePrinter.java new file mode 100644 index 0000000..43d9d29 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/ConstantPoolSizePrinter.java @@ -0,0 +1,70 @@ +/* + * Class: ConstantPoolSizePrinter + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +/** + * Prints the size of the constant pool. Neatly packaged into an Optimization so + * that it can be inserted at any point in the optimization cycle. + * + * @author Chris Bennetts + * + */ +public class ConstantPoolSizePrinter implements Optimization { + public void transform(MethodState state) { + int size = state.methodGen().getConstantPool().getSize(); + System.out.println("ConstantPool size: " + size); + } + + public String traceMessage(String dateString) { + return null; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + public static void print(MethodState state) { + (new ConstantPoolSizePrinter()).transform(state); + } +} diff --git a/src/edu/purdue/cs/bloat/optimize/Main.java b/src/edu/purdue/cs/bloat/optimize/Main.java new file mode 100644 index 0000000..e906ad3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/Main.java @@ -0,0 +1,921 @@ +/* + * Class: Main + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +import org.apache.bcel.util.ClassPath; +import org.apache.bcel.util.Repository; +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +import edu.purdue.cs.bloat.cfg.DominatorTree; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.VerifyCFG; +import edu.purdue.cs.bloat.codegen.CodeGenerator; +import edu.purdue.cs.bloat.codegen.Liveness; +import edu.purdue.cs.bloat.context.BloatContext; +import edu.purdue.cs.bloat.context.PersistentBloatContext; +import edu.purdue.cs.bloat.diva.InductionVarAnalyzer; +import edu.purdue.cs.bloat.editor.ClassHierarchy; +import edu.purdue.cs.bloat.inline.StackHeightCounter; +import edu.purdue.cs.bloat.ssa.SSA; +import edu.purdue.cs.bloat.ssa.SSAGraph; +import edu.purdue.cs.bloat.tbaa.TypeInference; +import edu.purdue.cs.bloat.trans.DeadCodeElimination; +import edu.purdue.cs.bloat.trans.ExprPropagation; +import edu.purdue.cs.bloat.trans.Peephole; +import edu.purdue.cs.bloat.trans.SSAPRE; +import edu.purdue.cs.bloat.trans.StackOpt; +import edu.purdue.cs.bloat.trans.StackOptimization; +import edu.purdue.cs.bloat.trans.ValueFolding; +import edu.purdue.cs.bloat.trans.ValueNumbering; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.codegen.RegisterAllocator; + +import java.io.*; +import java.text.*; +import java.util.*; + +/** + * Usage: java edu.purdue.cs.bloat.optimize.Main [-options] classes dir + * + * where options include: -help print out this message -v -verbose turn on + * verbose mode -debug display a hideous amount of debug info -classpath + * list directories in which to look for + * classes -f optimize files even if up-to-date -closure recursively optimize + * referenced classes -relax-loading don't report errors if a class is not found + * -skip skip the given class or package -only + * skip all but the given class or package -preserve-debug try + * to preserve debug information -[no]anno insert an annotation in the contant + * pool -[no]stack-alloc try to push locals onto the operand stack -peel-loops + * peel innermost loops to enable code hoisting (n >= 0 is the maximum + * loop level to peel) -[no]pre perform partial redundency elimination + * -[no]appre perform partial redundency elimination on access paths -[no]dce + * perform dead code elimination -diva perform demand-driven induction variable + * analysis -[no]prop perform copy and constant propagation + * + */ +public class Main { + // the default class path to read the classes from + static String DEFAULT_CLASSPATH = "."; + + // Flags that can be set/unset from the command line + static boolean DEBUG = false; // Display debugging information + + static boolean VERBOSE = false;// Display status information as program + + // runs + + public static boolean TRACE = false;// Track our progress + + static boolean FORCE = false;// Optimize file even if they are + + // up-to-date + + static boolean CLOSURE = false;// Opimtize over entire heirarchy (i.e. + + // start + + // at the specified classes and recurse up the + // class heirarchy) + + static DateFormat dateFormat = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.LONG); + + static boolean DIVA = false; + + public static boolean PRE = true; + + public static boolean DCE = true; + + public static boolean PROP = true; + + public static boolean FOLD = true; // Value folding + + public static boolean INFER = true; // Type inference + + public static boolean NUMBER = true; // Value numbering + + public static boolean PERSIST = false; // Persistent check elimination + + public static boolean STACK_ALLOC = false; + + static boolean ANNO = true; // Note that class file was optimized + + public static boolean VERIFY = true; + + public static boolean OPT_STACK_1 = false; // Perform stack optimizations + + public static boolean OPT_STACK_2 = false; + + static String[] ARGS = null; // Arguments from the command line + + public static List SKIP = new ArrayList(); + + // Classes that are specifically not optimized + static List ONLY = new ArrayList(); + + // Classes that are specifically optimized + static String METHOD = null; // The name of one method to edit + + static BloatContext context = null; + + // static ClassFileLoader loader = null;//Used to load classes from + // class files + static Repository loader = null; + + static File outputDir = null; + + public static List optimizationsToPerform = null; + + /** + * Parses the command line. The user must specify at least one class to + * optimize and the directory in which to place the optimized class files. + * The methods of the specified classes are then optimized according to the + * command line options. + * + * @see ClassEditor + * @see ClassFileLoader + * @see ClassFile + * @see MethodEditor + * @see MethodInfo + * + * @see CompactArrayInitializer + * @see FlowGraph + * + */ + public static void main(String[] args) { + try { + ClassPath classPath = new ClassPath(DEFAULT_CLASSPATH); + + List classes = new ArrayList(args.length); // The classes to + // optimize + boolean gotdir = false; // Has an output directory been + // specified? + + ARGS = args; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + VERBOSE = true; + + } else if (args[i].equals("-debug")) { + DEBUG = true; + FlowGraph.DEBUG = true; + DominatorTree.DEBUG = true; + Tree.DEBUG = true; + CodeGenerator.DEBUG = true; + Liveness.DEBUG = true; + SSA.DEBUG = true; + SSAGraph.DEBUG = true; + // PersistentCheckElimination.DEBUG = true; + ValueNumbering.DEBUG = true; + ValueFolding.DEBUG = true; + ClassHierarchy.DEBUG = true; + TypeInference.DEBUG = true; + SSAPRE.DEBUG = true; + ExprPropagation.DEBUG = true; + DeadCodeElimination.DEBUG = true; + CodeGenerator.DB_OPT_STACK = true; + } else if (args[i].equals("-trace")) { + TRACE = true; + } else if (args[i].equals("-db")) { + + if (++i >= args.length) { + System.err.println("** No debugging option specified"); + usage(); + } + + if (args[i].equals("bc")) { + // CodeArray.DEBUG = true; + + } else if (args[i].equals("cfg")) { + FlowGraph.DEBUG = true; + + } else if (args[i].equals("ssa")) { + SSA.DEBUG = true; + SSAGraph.DEBUG = true; + + } else if (args[i].equals("graphs")) { + FlowGraph.DB_GRAPHS = true; + + } else if (args[i].startsWith("-")) { + i--; + + } else { + System.err.println("** Unknown debugging option: " + + args[i]); + usage(); + } + + } else if (args[i].equals("-debugvf")) { + ValueFolding.DUMP = true; + + } else if (args[i].equals("-debugbc")) { + // BloatContext.DEBUG = true; + + } else if (args[i].equals("-help")) { + usage(); + + } else if (args[i].equals("-noanno")) { + ANNO = false; + + } else if (args[i].equals("-anno")) { + ANNO = true; + + } else if (args[i].equals("-print-flow-graph")) { + FlowGraph.PRINT_GRAPH = true; + + } else if (args[i].equals("-preserve-debug")) { + // CodeGenerator.PRESERVE_DEBUG = true; + + } else if (args[i].equals("-nouse-stack-vars")) { + Tree.USE_STACK = false; + + } else if (args[i].equals("-use-stack-vars")) { + Tree.USE_STACK = true; + + } else if (args[i].equals("-unique-handlers")) { + // CodeGenerator.UNIQUE_HANDLERS = true; + + } else if (args[i].equals("-nostack-alloc")) { + STACK_ALLOC = false; + + } else if (args[i].equals("-stack-alloc")) { + STACK_ALLOC = true; + + } else if (args[i].equals("-no-verify")) { + VERIFY = false; + + } else if (args[i].equals("-peel-loops")) { + if (++i >= args.length) { + usage(); + } + + String n = args[i]; + + if (n.equals("all")) { + FlowGraph.PEEL_LOOPS_LEVEL = FlowGraph.PEEL_ALL_LOOPS; + + } else { + try { + FlowGraph.PEEL_LOOPS_LEVEL = Integer.parseInt(n); + + if (FlowGraph.PEEL_LOOPS_LEVEL < 0) { + usage(); + } + } catch (NumberFormatException ex) { + usage(); + } + } + + } else if (args[i].equals("-color")) { + Liveness.UNIQUE = false; + + } else if (args[i].equals("-nocolor")) { + Liveness.UNIQUE = true; + + } else if (args[i].equals("-only-method")) { + if (++i >= args.length) { + usage(); + } + + METHOD = args[i]; + + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + usage(); + } + + classPath = new ClassPath(args[i]); + } else if (args[i].equals("-classpath/p")) { + if (++i >= args.length) { + usage(); + } + + classPath = new ClassPath(args[i]); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + usage(); + } + + String pkg = args[i]; + + // Account for class file name on command line + if (pkg.endsWith(".class")) + pkg = pkg.substring(0, pkg.lastIndexOf('.')); + + SKIP.add(pkg.replace('.', '/')); + + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + usage(); + } + + String pkg = args[i]; + + // Account for class file name on command line + if (pkg.endsWith(".class")) + pkg = pkg.substring(0, pkg.lastIndexOf('.')); + + ONLY.add(pkg.replace('.', '/')); + + } else if (args[i].equals("-nodce")) { + DCE = false; + + } else if (args[i].equals("-noprop")) { + PROP = false; + + } else if (args[i].equals("-noappre")) { + SSAPRE.NO_ACCESS_PATHS = true; + + } else if (args[i].equals("-nopre")) { + PRE = false; + + } else if (args[i].equals("-dce")) { + DCE = true; + + } else if (args[i].equals("-prop")) { + PROP = true; + + } else if (args[i].equals("-appre")) { + SSAPRE.NO_ACCESS_PATHS = false; + + } else if (args[i].equals("-pre")) { + PRE = true; + + } else if (args[i].equals("-closure")) { + CLOSURE = true; + + } else if (args[i].equals("-opt-stack-1")) { + OPT_STACK_1 = true; + CodeGenerator.OPT_STACK = true; + + } else if (args[i].equals("-opt-stack-2")) { + OPT_STACK_2 = true; + CodeGenerator.OPT_STACK_2 = true; + + } else if (args[i].equals("-diva")) { + DIVA = true; + + } else if (args[i].equals("-no-thread")) { + SSAPRE.NO_THREAD = true; + + } else if (args[i].equals("-no-precise")) { + SSAPRE.NO_PRECISE = true; + + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + + } else if (args[i].equals("-f") || args[i].equals("-force")) { + FORCE = true; + + } else if (args[i].startsWith("-")) { + System.err.println("No such option: " + args[i]); + usage(); + + } else if (i == args.length - 1) { + // Last argument is the name of the output directory + + File f = new File(args[i]); + + if (f.exists() && !f.isDirectory()) { + System.err.println("No such directory: " + f.getPath()); + System.exit(2); + } + + if (!f.exists()) { + f.mkdirs(); + } + + if (!f.exists()) { + System.err.println("Couldn't create directory: " + + f.getPath()); + System.exit(2); + } + + // Tell class loader to put optimized classes in f + // directory + outputDir = f; + gotdir = true; + } else { + // The argument must be a class name... + classes.add(args[i]); + } + } + + loader = org.apache.bcel.util.SyntheticRepository + .getInstance(classPath); + + if (!gotdir) { + System.err.println("No output directory specified"); + usage(); + } + + if (classes.size() == 0) { + System.err.println("** No classes specified"); + usage(); + } + + if (TRACE) + System.setProperty("edu.purdue.cs.bloat.TRACE", "true"); + if (DEBUG) + System.setProperty("edu.purdue.cs.bloat.DEBUG", "true"); + + // Use the CachingBloatingContext + context = new PersistentBloatContext(loader, CLOSURE); + + buildOptimizationsToPerform(); + + boolean errors = false; + + Iterator iter = classes.iterator(); + + // Now that we've parsed the command line, load the classes into + // the + // class loader + while (iter.hasNext()) { + String name = (String) iter.next(); + + try { + context.loadClass(name); + + } catch (ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + if (!CLOSURE) { + Iterator e = classes.iterator(); + + // Edit only the classes that were specified on the command line + + while (e.hasNext()) { + String name = (String) e.next(); + editClass(name); + } + } else { + // Edit all the classes in the class file editor and their + // superclasses + + classes = null; + + if (TRACE) { + System.out.println("Computing closure " + + dateFormat.format(new Date())); + } + + Iterator e = context.getHierarchy().classes().iterator(); + + while (e.hasNext()) { + Type t = (Type) e.next(); + + if (t instanceof ObjectType) { + editClass(((ObjectType) t).getClassName()); + } + } + } + } catch (ExceptionInInitializerError ex) { + ex.printStackTrace(); + System.out.println(ex.getException()); + } + } + + private static void usage() { + System.err + .println("\nUsage: java edu.purdue.cs.bloat.optimize.Main" + + "\n [-options] classes dir" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -v -verbose turn on verbose mode" + + "\n -debug display a hideous amount of debug info" + + "\n -classpath " + + "\n list directories in which to look for classes" + + "\n -f optimize files even if up-to-date" + + "\n -closure recursively optimize referenced classes" + + "\n -relax-loading don't report errors if a class is not found" + + "\n -skip " + + "\n skip the given class or package" + + "\n -only " + + "\n skip all but the given class or package" + + "\n -preserve-debug try to preserve debug information" + + "\n -[no]anno insert an annotation in the contant pool" + + "\n -[no]stack-alloc try to push locals onto the operand stack" + + "\n -peel-loops " + + "\n peel innermost loops to enable code hoisting" + + "\n (n >= 0 is the maximum loop level to peel)" + + "\n -[no]pre perform partial redundency elimination" + + "\n -[no]dce perform dead code elimination" + + "\n -diva perform demand-driven induction variable analysis" + + "\n -[no]prop perform copy and constant propagation" + + ""); + System.exit(0); + } + + /** + * Performs the actual editing of a class. Does a whole mess of stuff + * including reading in the classfile, building data structures to represent + * the class file, converting the CFG for each method in the class into SSA + * form, perform some anlayses and optimizations on the method, and finally + * committing it back to the class file. Phew. + */ + private static void editClass(String className) { + JavaClass classFile; // Holds info about a class (implements + // ClassInfo) + File targetFile = null; + // Get information about the class className + try { + classFile = (JavaClass) context.loadClass(className); + } catch (ClassNotFoundException ex) { + System.err.println("** Couldn't find class: " + ex.getMessage()); + return; + } + + // Check to see if the file is up-to-date (i.e. has been + // recompiled since it was last optimized). If so, do nothing + // because the FORCE flag is false. + String source = classFile.getSourceFileName(); + System.out.println(source + " source file for class:" + + classFile.getClassName()); + File sourceFile = new File(source); + + String target = outputDir.getAbsolutePath() + + File.separatorChar + + classFile.getClassName().substring( + classFile.getClassName().lastIndexOf(".") + 1) + + ".class"; + System.out.println(target + " selected for output of class: " + + classFile.getClassName()); + targetFile = new File(target); + + if (!FORCE) { + if (sourceFile != null && targetFile != null && sourceFile.exists() + && targetFile.exists() + && sourceFile.lastModified() < targetFile.lastModified()) { + if (VERBOSE) { + System.out.println(classFile.getClassName() + + " is up to date"); + } + + return; + } + // return; + } + + if (DEBUG) { + // Print the contents of the class file to System.out + System.out.println(classFile); + } + + ClassGen c = context.editClass(classFile); + + boolean skip = false; + + String name = c.getClassName(); + String qual = ""; + if (name.lastIndexOf('/') != -1) + qual = name.substring(0, name.lastIndexOf('/')) + "/*"; + + // Edit only classes explicitly mentioned. + if (ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < ONLY.size(); i++) { + String pkg = (String) ONLY.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = false; + break; + } + } + } + + // Don't edit classes we explicitly skip. + if (!skip) { + for (int i = 0; i < SKIP.size(); i++) { + String pkg = (String) SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (VERBOSE) { + System.out.println("Skipping " + c.getClassName()); + } + + // We're done with this class file, decrement its reference + // count + context.release(classFile); + return; + } + + // Touch the output file first. That is, create the file, but make + // it empty, just to make sure we can create it. + // File outputDir = loader.outputDir(); + + if (VERBOSE) { + System.out.println("Optimizing " + c.getClassName()); + } + + // Finally, we can start playing with the methods... + Method[] methods = c.getMethods(); + + int numMethods = methods.length + 1; + ; + int whichMethod = 0; + + for (int j = 0; j < methods.length; j++) { + final MethodGen m; + + try { + m = context.editMethod(methods[j], c.getClassName()); + } catch (ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + if (TRACE) { + whichMethod++; + System.out + .println("Optimizing " + name + "." + m.getName() + + " (method " + whichMethod + " of " + + numMethods + ")"); + } + + if (METHOD != null) { + // A method name has been specified on the command line using + // -only-method. + boolean pass = true; + + String t = m.getName() + m.getSignature(); + + if (t.equals(METHOD)) { + pass = false; + } + + t = m.getName(); + + if (t.equals(METHOD)) { + pass = false; + } + + if (pass) { + // This isn't the method we're looking for. + // Decrement its reference count. + context.release(methods[j], c.getClassName()); + continue; + } + } + + if (DEBUG) { + System.out.println(m); + } + + if (m.isNative() || m.isAbstract()) { + // We can't edit native or abstract methods + context.release(methods[j], c.getClassName()); + continue; + } + + MethodBloater bloater = new MethodBloater(optimizationsToPerform); + bloater.bloatMethod(m, context); + } + + if (ANNO) { + String s = "Optimized with: edu.purdue.cs.bloat.optimize.Main"; + + for (int i = 0; i < ARGS.length; i++) { + if (ARGS[i].indexOf(' ') >= 0 || ARGS[i].indexOf('\t') >= 0 + || ARGS[i].indexOf('\r') >= 0 + || ARGS[i].indexOf('\n') >= 0) { + s += " '" + ARGS[i] + "'"; + } else { + s += " " + ARGS[i]; + } + } + + System.out.println(s); + } + + try { + classFile = context.editClass(classFile.getClassName()) + .getJavaClass(); + } catch (ClassNotFoundException cnfe) { + } + + try { + classFile.dump(targetFile); + } catch (java.io.IOException io) { + System.out.println("An error occured whilst writing file: " + + targetFile.getPath()); + } + if (TRACE) + System.out.println(context.toString()); + } + + public static void dumpcode(MethodGen m) { + PrintWriter out = new PrintWriter(System.out, true); + StackHeightCounter shc = new StackHeightCounter(m); + + out.println("Code for method " + m.getName() + m.getSignature()); + InstructionList instructions = m.getInstructionList(); + Iterator iter = instructions.iterator(); + while (iter.hasNext()) { + Object obj = iter.next(); + shc.handle((InstructionHandle) obj); + + System.out + .println(" " + obj + " (sh: " + shc.height() + ")"); + } + } + + /* + * The following block of code is a version of buildOptimizationsToPerform + * that does not use OptimizationsBuilder. If you revert to this version, + * you will have to uncomment the last couple of lines of + * MethodBloater#doAnOptimization(). + * + * private static void buildOptimizationsToPerform() { + * optimizationsToPerform = new ArrayList(); + * + * if (COMPACT_ARRAY_INIT) optimizationsToPerform.add(new + * CompactArrayInitializer()); + * + * optimizationsToPerform.add(new SSA()); + * + * if ((! Tree.USE_STACK) && PROP) optimizationsToPerform.add(new + * ExprPropagation()); + * + * if (DCE) optimizationsToPerform.add(new DeadCodeElimination()); + * + * if (INFER) optimizationsToPerform.add(new TypeInference()); + * + * if (NUMBER) optimizationsToPerform.add(new ValueNumbering()); + * + * if (FOLD) optimizationsToPerform.add(new ValueFolding()); + * + * if (PRE) optimizationsToPerform.add(new SSAPRE()); + * + * if (FOLD) optimizationsToPerform.add(new ValueFolding()); + * + * if (PROP) optimizationsToPerform.add(new ExprPropagation()); // make sure + * we've done at least one thing since the last DCE if (DCE && (INFER || + * NUMBER || FOLD || PRE || PROP)) optimizationsToPerform.add(new + * DeadCodeElimination()); //doDeadCodeElimination(cfg, context); /* if + * (PERSIST) (new PersistentCheckElimination()).transform(cfg); + */ + /* + * if (DIVA) optimizationsToPerform.add(new InductionVarAnalyzer()); + * //doInductionVarAnalysis(cfg, context); + * + * if (OPT_STACK_2) { optimizationsToPerform.add(new StackOpt()); + * optimizationsToPerform.add(new SSA()); optimizationsToPerform.add(new + * DeadCodeElimination()); } + * + * if (CodeGenerator.OPT_STACK) { optimizationsToPerform.add(new + * StackOptimization()); } + * + * if (VERIFY) optimizationsToPerform.add(new VerifyCFG()); + * + * optimizationsToPerform.add(new CodeGen()); optimizationsToPerform.add(new + * ConstantPoolSizePrinter()); optimizationsToPerform.add(new Peephole()); } + */ + + public static void buildOptimizationsToPerform() { + + OptimizationsBuilder builder = new OptimizationsBuilder(); + + builder.add(new SSA()); + + if ((!Tree.USE_STACK) && PROP) + builder.add(new ExprPropagation()); + + if (DCE) + builder.add(new DeadCodeElimination()); + + if (INFER) + builder.add(new TypeInference()); + + if (NUMBER) + builder.add(new ValueNumbering()); + + if (FOLD) + builder.add(new ValueFolding()); + + if (PRE) + builder.add(new SSAPRE()); + + if (FOLD) + builder.add(new ValueFolding()); + + if (PROP) + builder.add(new ExprPropagation()); + + // make sure we've done at least one thing since the last DCE + if (DCE && (INFER || NUMBER || FOLD || PRE || PROP)) + builder.add(new DeadCodeElimination()); + + /* + * if (PERSIST) (new PersistentCheckElimination()).transform(cfg); + */ + + if (DIVA) + builder.add(new InductionVarAnalyzer()); + + if (OPT_STACK_2) { + builder.add(new StackOpt()); + builder.add(new SSA()); + builder.add(new DeadCodeElimination()); + } + RegisterAllocator alloc = new RegisterAllocator(); + builder.add(alloc); + + if (CodeGenerator.OPT_STACK) { + builder.add(new StackOptimization()); + } + + if (VERIFY) + builder.add(new VerifyCFG()); + + builder.add(new CodeGen(alloc)); + // builder.add(new ConstantPoolSizePrinter()); + builder.add(new Peephole()); + + optimizationsToPerform = builder.getList(); + } + + /** + * OptimizationsBuilder is a little utility class intended to simplify the + * creation of the list of optimizations. In particular, it will + * automatically add a VerifyCFG call after each optimization whenever we're + * in debug mode. This simplifies the creation of the list, and + * {@link MethodBloater#doAnOptimization(Optimization, MethodState)} too. + * + * @author Chris Bennetts + * + */ + static class OptimizationsBuilder { + private List _optimizations = new ArrayList(); + + public List getList() { + return _optimizations; + } + + public OptimizationsBuilder add(Optimization opt) { + _optimizations.add(opt); + if (DEBUG) + _optimizations.add(new VerifyCFG()); + + return this; + } + } +} diff --git a/src/edu/purdue/cs/bloat/optimize/Makefile b/src/edu/purdue/cs/bloat/optimize/Makefile new file mode 100644 index 0000000..522976a --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/Makefile @@ -0,0 +1,31 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + CodeGen.class \ + ConstantPoolSizePrinter.class \ + Main.class \ + MethodBloater.class \ + MethodState.class \ + Optimization.class + +include ../class.mk + diff --git a/src/edu/purdue/cs/bloat/optimize/MethodBloater.java b/src/edu/purdue/cs/bloat/optimize/MethodBloater.java new file mode 100644 index 0000000..8e07be9 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/MethodBloater.java @@ -0,0 +1,243 @@ +/* + * Class: MethodBloater + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.apache.bcel.generic.LocalVariableGen; +import org.apache.bcel.generic.CodeExceptionGen; +import org.apache.bcel.generic.MethodGen; + +import edu.purdue.cs.bloat.context.BloatContext; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Performs BLOAT-ing on a method. A single MethodBloater object may be safely + * reused for multiple methods. + * + * Extra debugging information will be printed if the system property + * edu.purdue.cs.bloat.DEBUG is set to true. + * + * @author Chris Bennetts + * + */ +public class MethodBloater { + static DateFormat dateFormat = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.LONG); + + static boolean DEBUG = "true".equals(System + .getProperty("edu.purdue.cs.bloat.DEBUG")); + + List optimizationsToPerform; + + /** + * Create a new MethodBloater. + * + * @param optimizationsToPerform + * The optimizations to be performed. + */ + public MethodBloater(List optimizationsToPerform) { + this.optimizationsToPerform = optimizationsToPerform; + } + + /** + * BLOAT a single method. Creates the {@link MethodState method state}, + * runs the optimizations, and commits any changes back to the context + * object. + *

+ * + * Will return prematurely if there is a problem creating the control flow + * graph. + */ + public void bloatMethod(MethodGen methodGen, BloatContext context) { + try { + MethodBloater.printMethodGenInfo(methodGen); + + MethodState state = new MethodState(methodGen, context); + if (state.controlFlowGraph() == null) + return; + + performOptimizations(state); + state.commitChanges(); + + // ConstantPoolSizePrinter.print(state); + MethodBloater.printMethodGenInfo(methodGen); + } catch (IllegalArgumentException e) { + System.out.println(" NOTE: CFG did not verify while bloating " + + methodGen.getName() + + " after all optimizations. Exception: " + e); + e.printStackTrace(); + } catch (Exception e) { + String msg = "** Exception while optimizing " + methodGen.getName() + + methodGen.getSignature() + " of class " + + methodGen.getClassName(); + System.err.println(msg); + System.err.println(e.getMessage()); + e.printStackTrace(System.err); + System.exit(1); + } + } + + /** + * Performs all optimizations on the method. + * + * @param state + * The method to perform the optimizations on. + * @throws Exception + * Any exception that may be thrown by the optimizations. + */ + public void performOptimizations(MethodState state) throws Exception { + Assert.isNotNull(optimizationsToPerform); + + Iterator it = optimizationsToPerform.iterator(); + + while (it.hasNext()) { + Optimization opt = (Optimization) it.next(); + doAnOptimization(opt, state); + } + } + + /** + * Print the trace message for an optimization. + * + * @see Optimization#traceMessage(String) + */ + private void printTraceMessage(Optimization o) { + if (!DEBUG) + return; + + String dateString = dateFormat.format(new Date()); + String message = o.traceMessage(dateString); + + if (message != null) + System.out.println(message); + } + + /** + * Print a debug message for an optimization. Called before the + * transformation is performed. + * + * @see Optimization#preDebugMessage() + */ + private void printPreDebugMessage(Optimization o) { + if (!DEBUG) + return; + + String message = o.preDebugMessage(); + + if (message != null) + System.out.println(message); + } + + /** + * Print a debug message for an optimization. Called after the + * transformation is performed. + * + * @see Optimization#postDebugMessage() + */ + private void printPostDebugMessage(Optimization o) { + if (!DEBUG) + return; + + String message = o.postDebugMessage(); + + if (message != null) + System.out.println(message); + } + + /** + * Apply an optimization to a method. + * + * @param optimization + * The optimization to be performed. + * @param state + * The method to perform the optimization on. + * @throws Exception + * Any exception thrown by the optimization. + * + * @see Optimization#transform(MethodState) + */ + private void doAnOptimization(Optimization optimization, MethodState state) + throws Exception { + printTraceMessage(optimization); + printPreDebugMessage(optimization); + + optimization.transform(state); + + printPostDebugMessage(optimization); + + // FIXME: VerifyCFG can itself now be called as an Optimization. + // Use a Builder or something to encapsulate this bit of behaviour. + // This is currently implemented by Main.OptimizationsBuilder, and + // MainG2 allows it to just be inserted into the list of optimizations. + + // if (DEBUG) + // VerifyCFG.verify(state.controlFlowGraph()); + } + + /** + * Print some debugging info about the MethodGen. + */ + static void printMethodGenInfo(MethodGen methodGen) { + if (!DEBUG) + return; + + LocalVariableGen[] locals = null; + CodeExceptionGen[] excptns = null; + System.out.println("Method: " + methodGen); + System.out.println(methodGen.getMethod().getCode()); + System.out.println(); + locals = methodGen.getLocalVariables(); + if (locals != null) { + System.out.println("LocalVariables: "); + for (int j = 0; j < locals.length; j++) + System.out.println(" " + locals[j]); + } + excptns = methodGen.getExceptionHandlers(); + if (excptns != null) { + System.out.println("ExceptionHandlers: "); + for (int j = 0; j < excptns.length; j++) + System.out.println(" " + excptns[j]); + } + } +} diff --git a/src/edu/purdue/cs/bloat/optimize/MethodState.java b/src/edu/purdue/cs/bloat/optimize/MethodState.java new file mode 100644 index 0000000..70e919e --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/MethodState.java @@ -0,0 +1,140 @@ +/* + * Class: MethodState + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +import org.apache.bcel.classfile.ClassFormatException; +import org.apache.bcel.generic.MethodGen; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.context.BloatContext; + +/** + * MethodState encapsulates three related items for a method: + * + *

    + *
  • its MethodGen,
  • + *
  • its Control Flow Graph,
  • + *
  • its context.
  • + *
+ * + * These tend to be passed around and updated together, so it made sense to + * group them together. + * + * Note that this class has no "setters". If they appear necessary, + * move the code that would need them in to this class, instead of taking the + * shortcut. + * + * @author Chris Bennetts + * + */ +public class MethodState { + private MethodGen _methodGen; + + private FlowGraph _controlFlowGraph; + + private BloatContext _context; + + /** + * Create a new MethodState object. The control flow graph is generated + * automatically. + */ + public MethodState(MethodGen methodGen, BloatContext context) { + _methodGen = methodGen; + _context = context; + + rebuildFlowGraph(); + } + + /** + * Obtain the MethodGen. + */ + public MethodGen methodGen() { + return _methodGen; + } + + /** + * Obtain the control flow graph. In general, favour + * {@link #controlFlowGraph() controlFlowGraph()}. + */ + public FlowGraph cfg() { + return controlFlowGraph(); + } + + /** + * Obtain the control flow graph. + */ + public FlowGraph controlFlowGraph() { + return _controlFlowGraph; + } + + /** + * Obtain the current context. + */ + public BloatContext context() { + return _context; + } + + /** + * Rebuild the control flow graph. Use if it falls out of sync with the + * MethodGen. + * + */ + public void rebuildFlowGraph() { + try { + _controlFlowGraph = new FlowGraph(_methodGen); + } catch (ClassFormatException ex) { + System.err.println(ex.getMessage()); + _context.release(_methodGen.getMethod(), _methodGen.getClassName()); + _controlFlowGraph = null; + + return; + } + + _controlFlowGraph.initialize(); + } + + /** + * Commit changes to the method to the context. + * + */ + public void commitChanges() { + _context.commit(_methodGen); + } +} diff --git a/src/edu/purdue/cs/bloat/optimize/Optimization.java b/src/edu/purdue/cs/bloat/optimize/Optimization.java new file mode 100644 index 0000000..15e8ae0 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/Optimization.java @@ -0,0 +1,90 @@ +/* + * Class: Optimization + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.optimize; + +/** + * Optimization provides a uniform interface to all transformations provided by + * BLOAT. As well as the transform() method, Optimization also specifies a + * number of trace/debug methods. + * + * There's no actual requirement for Optimizations to actually transform the + * code. It is perfectly acceptable to create "optimizations" that print some + * kind of debugging information without actually changing the method state. + * + * In addition to the four methods that this interface declares, all + * implementers must provide a public zero-argument constructor. + * + * @author Chris Bennetts + * + */ +public interface Optimization { + + /** + * Perform the optimization. + * + * @param state + * @throws Exception + */ + void transform(MethodState state) throws Exception; + + /** + * Print a trace message. Called before #preDebugMessage(). + * + * @param dateString + * The current timestamp. + * + * @return + */ + String traceMessage(String dateString); + + /** + * Print a debugging message. Called before #transform(). + * + * @return + */ + String preDebugMessage(); + + /** + * Print a debugging message. Called after #transform(). + * + * @return + */ + String postDebugMessage(); +} diff --git a/src/edu/purdue/cs/bloat/optimize/package.html b/src/edu/purdue/cs/bloat/optimize/package.html new file mode 100644 index 0000000..c53a2e3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/optimize/package.html @@ -0,0 +1,7 @@ + + + +

Contains a program that optimizes Java classes.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/reflect/ClassFormatException.java b/src/edu/purdue/cs/bloat/reflect/ClassFormatException.java new file mode 100644 index 0000000..ed8f5cf --- /dev/null +++ b/src/edu/purdue/cs/bloat/reflect/ClassFormatException.java @@ -0,0 +1,50 @@ +/* + * Class: ClassFormatException + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.reflect; + +public class ClassFormatException extends RuntimeException { + // serialVersionUID is strongly recommended for all classes implementing + // Serializable + public static final long serialVersionUID = 1L; + + public ClassFormatException(String msg) { + super(msg); + } +} diff --git a/src/edu/purdue/cs/bloat/reflect/Makefile b/src/edu/purdue/cs/bloat/reflect/Makefile new file mode 100644 index 0000000..1a02a3c --- /dev/null +++ b/src/edu/purdue/cs/bloat/reflect/Makefile @@ -0,0 +1,25 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + ClassFormatException.class\ + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/reflect/package.html b/src/edu/purdue/cs/bloat/reflect/package.html new file mode 100644 index 0000000..a1040ac --- /dev/null +++ b/src/edu/purdue/cs/bloat/reflect/package.html @@ -0,0 +1,14 @@ + + + + +

Provides an abstract API for working with Java classfiles. In this +package, names and types are represent by indices into the constant +pool. Modifier flags, the constant pool, exception handlers, and +debugging information are also modeled directly in this package. The +classes and interfaces in this package exist to give an opaque and +abstract view of the underlying representation of the Java class.

+ + + + diff --git a/src/edu/purdue/cs/bloat/ssa/ComponentVisitor.java b/src/edu/purdue/cs/bloat/ssa/ComponentVisitor.java new file mode 100644 index 0000000..fa1301d --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/ComponentVisitor.java @@ -0,0 +1,52 @@ +/* + * Class: ComponentVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.ssa; + +import java.util.*; + +/** + * ComponentVisitor is used to visit the strongly connected components (SSC) of + * a control flow graph. + * + * @see SSAGraph + */ +public abstract class ComponentVisitor { + public abstract void visitComponent(List component); +} diff --git a/src/edu/purdue/cs/bloat/ssa/Makefile b/src/edu/purdue/cs/bloat/ssa/Makefile new file mode 100644 index 0000000..bf12c3c --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/Makefile @@ -0,0 +1,29 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + PhiReturnStmt.class\ + SSAConstructionInfo.class\ + ComponentVisitor.class\ + SSAGraph.class\ + SSA.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/ssa/PhiReturnStmt.java b/src/edu/purdue/cs/bloat/ssa/PhiReturnStmt.java new file mode 100644 index 0000000..68348c7 --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/PhiReturnStmt.java @@ -0,0 +1,162 @@ +/* + * Class: PhiReturnStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.ssa; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Subroutine; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; + +/** + * A PhiReturnStmt is placed at the return site of a subroutine. This is + * necessary because variables that are not referrenced inside a subroutine + * (finally block) retain their value. This is a problem for SSA when a variable + * is assigned to inside an exception handler. At the beginning of the finally + * block, there would be a merge of two occurrences of the variable (one from + * the the exception handler and another from the "outside world") and a phi + * function should be placed accordingly. If the type of the variable was + * changed inside the exception handler, the operands of phi function would be + * of different types and that would be bad. To avoid this situation + * PhiReturnStmt are placed before the next instruction to be executed after the + * subroutine has returned. Note that each PhiReturnStmt has only one operand + * that is the same variable as its target. The two variables will have + * different version numbers, however. + *

+ * The following diagram demonstrates PhiReturnStmt: + * + *

+ *                               a1 = 1   a2 = 2
+ *                               b1 = 1   b2 = 2
+ *                                 jsr     jsr
+ *                                   \     /
+ *                                    \   /
+ *                                     \ /
+ *                                      *
+ *                            a3 = PhiJoinStmt(a1, a2)
+ *                            b3 = PhiJoinStmt(b1, b2)
+ *                                      |
+ *                                   b4 = 4  // b is defined in subrountine
+ *                                      |
+ *                                     ret
+ *                                      *
+ *                                     / \
+ *                                    /   \
+ *                                   /     \
+ *                                  /       \
+ *                 a4 = PhiReturnStmt(a3)  a5 = PhiReturnStmt(a3)
+ *                 b5 = PhiReturnStmt(b4)  b6 = PhiReturnStmt(b4)
+ * 
+ * + * After transformation, the PhiReturnStmts will become + * + *
+ *                          a1                    a2
+ *                          b4                    b4
+ * 
+ * + * The variable a is not modified in the subroutine, so it retains + * its value from before the jsr. The variable b is modified in the + * subroutine, so its value after the ret is the value it was assigned in the + * subroutine. + */ +class PhiReturnStmt extends PhiStmt { + final Subroutine sub; + + final Expr operand; + + /** + * Constructor. + * + * @param target + * Local variable to which the result of this phi statement is to + * be assigned. + * @param sub + * The subroutine from which we are returning. + */ + public PhiReturnStmt(VarExpr target, Subroutine sub) { + super(target); + this.sub = sub; + this.operand = (VarExpr) target.clone(); + operand.setParent(this); + operand.setDef(null); + } + + public void visitForceChildren(TreeVisitor visitor) { + operand.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitChildren(visitor); + } + + /** + * Returns the subroutine associated with this PhiReturnStmt. + */ + public Subroutine sub() { + return sub; + } + + /** + * Returns a collection containing the operands to the phi statement. In + * this case the collection contains the one operand. + */ + public Collection operands() { + ArrayList v = new ArrayList(); + v.add(operand); + return v; + } + + /** + * Returns the operand of this PhiReturnStmt statement. A + * PhiReturnStmt has only one operand because the block that + * begins an exception handler may have only one incoming edge (critical + * edges were split). + */ + public Expr operand() { + return operand; + } + + public String toString() { + return "" + target() + " := Phi-Return(" + operand + ", " + sub + ")"; + } +} diff --git a/src/edu/purdue/cs/bloat/ssa/SSA.java b/src/edu/purdue/cs/bloat/ssa/SSA.java new file mode 100644 index 0000000..b010cf8 --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/SSA.java @@ -0,0 +1,594 @@ +/* + * Class: SSA + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.ssa; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Handler; +import edu.purdue.cs.bloat.cfg.Subroutine; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.tree.DefExpr; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Compute the SSA form of the control flow graph and build FUD chains. + *

+ * The SSA algorithm is from: + * + *

+ *       R. Cytron, J. Ferrante J, B. K. Rosen, M. N. Wegman, and F. K. Zadeck,
+ *       "Efficiently Computing Static Single Assignment Form and the Control
+ *       Dependence Graph", TOPLAS, 13(4): 451-490, October 1991.
+ * 
+ * + *

+ * I made modifications to the algorithm to compute FUD chains and to run the + * algorithm separately for each variable similar to the SSAPRE algorithm. + * Making a separate pass for each variable allows variables to be added + * incrementally. + */ +public class SSA implements Optimization { + public static boolean DEBUG = false; + + public void transform(MethodState state) { + transform(state.controlFlowGraph()); + } + + /** + * Transforms a control flow graph into Single Static Assignment (SSA) form. + * First, the CFG is traversed and a list of all variables (both local and + * stack) eligible for SSA renaming is compiled. Variables are represented + * by instances of SSAConstructionInfo. Each of these variables + * is then transformed. + * + * @see #transform + * @see SSAConstructionInfo + */ + private void transform(FlowGraph cfg) { + Iterator e = collectVars(cfg); + + while (e.hasNext()) { + SSAConstructionInfo info = (SSAConstructionInfo) e.next(); + transform(cfg, info); + } + } + + /** + * Performs the actions necessary to convert a CFG into SSA form with + * respect to one variable. The variable's information is stored in the + * SSAConstructionInfo. + */ + public void transform(FlowGraph cfg, SSAConstructionInfo info) { + if (DEBUG) { + System.out.println("transforming " + info.prototype + " (" + + info.prototype.type() + ")"); + } + + placePhiFunctions(cfg, info); + rename(cfg, info); + insertCode(cfg, info); + } + + /** + * Visits the nodes in a control flow graph and constructs + * SSAConstructionInfo objects for each variable in the CFG. + * Returns the SSAConstructionInfos for the variables in the + * CFG. + */ + private Iterator collectVars(final FlowGraph cfg) { + // SSAConstructionInfo objects for cfg + final Map infos = new LinkedHashMap(); + + cfg.visit(new TreeVisitor() { + // Visit all statements in the CFG. Remove any pre-existing + // PhiStmts. + public void visitTree(Tree tree) { + Iterator iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + Stmt stmt = (Stmt) iter.next(); + + if (stmt instanceof PhiStmt) { + iter.remove(); + + } else { + stmt.visit(this); + } + } + } + + // Recall that VarExprs represent variables. If we have not + // already created a SSAConstructionInfo for a variable + // (VarExpr), do so. Make note of the fact that this is a real + // occurrence of the variable. + public void visitVarExpr(VarExpr expr) { + expr.visitChildren(this); + + expr.setDef(null); + + Object key = expr.comparator(); + + SSAConstructionInfo info = (SSAConstructionInfo) infos.get(key); + + if (info == null) { + info = new SSAConstructionInfo(cfg, expr); + infos.put(key, info); + } + + info.addReal(expr); + } + }); + + return infos.values().iterator(); + } + + /** + * Places phi statements at the appropriate locations in the CFG. This + * implementation only places phi functions for variables that are live on + * entry to at least one block. That is, if a variable is only used within + * one block, we don't bother searching for a place to put phi functions for + * it. + * + * @param cfg + * The CFG in which phi functions are placed. + * @param info + * The variable for which phi functions will be placed. + */ + private void placePhiFunctions(FlowGraph cfg, SSAConstructionInfo info) { + if (DEBUG) { + System.out.println("Placing phi-functions for " + info); + } + + // Phis are only placed for variables which are live on entry to + // at least one block. + // + // This is the semi-pruned form described in "Practical + // Improvements to the Construction and Destruction of Static + // Single Assignment Form" by Briggs, Cooper, Harvey, Simpson + // + + // Blocks in which the variable in the SSAConstructionInfo is + // defined. That is, variables that are defined in this block. + BitSet killed = new BitSet(cfg.size()); + + // Is the variable used in more than one block? + boolean nonLocal = false; + + Iterator reals = info.reals().iterator(); + + // Look at all real (not in phi statement) occurrences of the + // variable in the SSAConstructionInfo. Determine which variables + // are live on entry to some basic block (i.e. "non-local"). If + // a variable is not live on entry to some basic block, it is only + // used within the block in which it is defined, so don't bother + // adding a phi statement for it. + while (reals.hasNext()) { + VarExpr real = (VarExpr) reals.next(); + + Block block = real.block(); // Block in which variable occurs + + if (real.isDef()) { + killed.set(cfg.preOrderIndex(block)); + + } else if (!killed.get(cfg.preOrderIndex(block))) { + // There is a use of the variable as an operand that is not + // defined in the block in which it occurs. Therefore, the + // variable is non-local. + nonLocal = true; + break; + } + } + + if (!nonLocal) { + return; + } + + // We've decided that this variable is used in multiple blocks, + // so go ahead and place phi functions for it. + + // Iterate over all of the catch blocks (blocks that begin an + // exception handler) in the CFG and instert PhiCatchStmts where + // appropriate. + Iterator catchBlocks = cfg.catchBlocks().iterator(); + + while (catchBlocks.hasNext()) { + Block block = (Block) catchBlocks.next(); + info.addCatchPhi(block); + info.addDefBlock(block); + } + + // Iterate over all of the subroutines (finally blocks) and insert + // PhiReturnStmts where appropriate. + Iterator subs = cfg.subroutines().iterator(); + + while (subs.hasNext()) { + Subroutine sub = (Subroutine) subs.next(); + info.addRetPhis(sub); + + Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + info.addDefBlock(path[1]); + } + } + + // Now we add real phi functions to the CFG. Phi functions are + // placed at the (blocks in the) iterated dominance fontier of each + // of the blocks containing a definition of the variable. + Iterator df = cfg.iteratedDomFrontier(info.defBlocks()).iterator(); + + while (df.hasNext()) { + Block block = (Block) df.next(); + + // Don't place phi-statements in the exit block because one of + // the operands will always have a null definition. + if (block != cfg.sink()) + info.addPhi(block); + } + } + + /** + * If the block resides in a protected region and there is a + * PhiCatchStmt for the variable in question in the handler of + * the exception thrown by the protected region (meaning that the variable + * is used in the protected region), the variable becomes an operand to the + * PhiCatchStmt. + * + * @param info + * The variable (LocalExpr) that we're dealing with + * @param block + * The block in a potentially protected region. If the block is + * indeed in a protected region, the occurrence of the the + * variable represented by info becomes an operand to the + * PhiCatchStmt at the beginning of the protected region's + * handler. + * @param def + * The defining occurrence of the variable stored in info. + */ + private void addCatchPhiOperands(SSAConstructionInfo info, Block block, + LocalExpr def) { + Iterator handlers = block.graph().handlers().iterator(); + + // Iterate over all of the exception handlers in the CFG. If + // the block we are dealing with is a protected block (that is, + // is inside a try block), then the variable represented by info + // becomes an operand to the PhiCatchStmt residing at the + // beginning of the protected block's handler. + while (handlers.hasNext()) { + Handler handler = (Handler) handlers.next(); + + if (handler.protectedBlocks().contains(block)) { + PhiCatchStmt phi = (PhiCatchStmt) info.phiAtBlock(handler + .catchBlock()); + + if (phi != null && !phi.hasOperandDef(def)) { + LocalExpr operand = (LocalExpr) info.prototype.clone(); + operand.setDef(def); // ??? + phi.addOperand(operand); + } + } + } + } + + /** + * The actual renamining is done by the search method. This method just + * takes care of PhiReturnStmts. + */ + private void rename(FlowGraph cfg, SSAConstructionInfo info) { + search(cfg, info, null, cfg.source()); + + // Eliminate PhiReturns by replacing their uses with the defs live + // at the end of the returning sub or live on the same path on entry + // to the sub (if the variable did not occur in the subroutine). + + // Examine each PhiReturnStmt in the CFG. Recall that + // PhiReturnStmts are "inserted" at blocks that begin exceptions + boolean changed = true; + + while (changed) { + changed = false; + + Iterator subs = cfg.subroutines().iterator(); + + while (subs.hasNext()) { + Subroutine sub = (Subroutine) subs.next(); + Iterator paths = sub.paths().iterator(); + + PhiJoinStmt entry = (PhiJoinStmt) info.phiAtBlock(sub.entry()); + + if (entry == null) { + // If there was no PhiJoinStmt for the variable in the + // subroutine, who cares? We don't. + continue; + } + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + + PhiReturnStmt ret = (PhiReturnStmt) info + .phiAtBlock(path[1]); + + if (ret != null) { + DefExpr def = ret.operand().def(); + + if (def != entry.target()) { + // If the operand of the PhiReturnStmt is + // different from + // the new SSA variable defined by the + // PhiCatchStmt at + // the beginning of the subroutine, then the + // variable + // was defined in the subroutine, so the operand + // to the + // PhiReturnStmt is the correct SSA variable. + // This is + // like the variable "b" in figure 3.5 in Nate's + // Thesis. + continue; + } + + // Replace all uses of the target of the PhiReturnStmt + // with the SSA variable corresponding to the block in + // which the jsr occured. This is like variable "a" in + // figure 3.5 in Nate's Thesis. + def = ((VarExpr) entry.operandAt(path[0])).def(); + + Iterator uses = ret.target().uses().iterator(); + + while (uses.hasNext()) { + VarExpr use = (VarExpr) uses.next(); + use.setDef(def); + } + + // The PhiReturnStmt is no longer needed + info.removePhiAtBlock(path[1]); + changed = true; + } + } + } + } + + Iterator subs = cfg.subroutines().iterator(); + + // Examine any remaining PhiReturnStmts. Replace all uses of the + // target of the PhiReturnStmt with its operand. + while (subs.hasNext()) { + Subroutine sub = (Subroutine) subs.next(); + + Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + + PhiReturnStmt ret = (PhiReturnStmt) info.phiAtBlock(path[1]); + + if (ret != null) { + DefExpr def = ret.operand().def(); + + Iterator uses = ret.target().uses().iterator(); + + while (uses.hasNext()) { + VarExpr use = (VarExpr) uses.next(); + use.setDef(def); + } + + info.removePhiAtBlock(path[1]); + } + } + } + } + + /** + * Does the actual renaming. It keeps track of the most recent occurrence of + * an (SSA numbered) variable and recalculates the definitions of variables + * appropriately. + * + * @param info + * SSAConstructionInfo representing the variable being converted + * into SSA form. + * @param top + * "Top" of the variable stack for the variable in question. Each + * variable has a "stack" associated with it. The top of the + * stack contains the current SSA name of the variable. It can + * also be thought of as the "most recent definition" of the + * variable. + * @param block + * Basic block in which the variable is being renamed. + */ + private void search(FlowGraph cfg, SSAConstructionInfo info, VarExpr top, + Block block) { + if (DEBUG) { + System.out.println("renaming " + info.prototype + " in " + block); + } + + // If appropriate, add top as an operand of a PhiCatchStmt + if (top instanceof LocalExpr) { + addCatchPhiOperands(info, block, (LocalExpr) top); + } + + // First handle any phi in the block. + PhiStmt phi = info.phiAtBlock(block); + + if (phi != null) { + top = phi.target(); + + if (top instanceof LocalExpr) { + addCatchPhiOperands(info, block, (LocalExpr) top); + } + } + + // If the block in which the variable is being renamed begins an + // exception handler and we're dealing with a stack variable, then + // there is no most recent definition of the variable because the + // stack is cleared when an exception is handled. I dunno. + if (cfg.catchBlocks().contains(block) + && info.prototype instanceof StackExpr) { + + if (DEBUG) { + System.out.println(" Killing TOS at " + block); + } + + // The operand stack is popped down to 0 at catch blocks. + top = null; + } + + Iterator e = info.realsAtBlock(block).iterator(); + + // Examine each occurrence of the variable in the block of + // interest. When we encounter a definition of the variable, make + // that definition to the most recent SSA variable (top). For + // each use, make this most recent SSA variable be its defining + // expression. + while (e.hasNext()) { + VarExpr real = (VarExpr) e.next(); + + if (real.isDef()) { + real.setDef(null); + + top = real; // A definition means a new SSA variable + + if (top instanceof LocalExpr) { + addCatchPhiOperands(info, block, (LocalExpr) top); + } + + if (DEBUG) { + System.out.println(" TOS = " + top); + } + + } else { + // Make sure that the variable is defined somewhere else + // (somewhere that we have already seen). + Assert.isTrue(top != null, "Null def for " + real); + real.setDef(top); + } + } + + Iterator succs = cfg.succs(block).iterator(); + + // Examine all successors of the block in question. If the + // successor contains a PhiJoinStmt for the variable, then set the + // operand corresponding to the block to be defined by the most + // recent SSA variable. Similarly for a PhiReturnStmt. + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + PhiStmt succPhi = info.phiAtBlock(succ); + + if (succPhi instanceof PhiJoinStmt) { + PhiJoinStmt f = (PhiJoinStmt) succPhi; + VarExpr operand = (VarExpr) f.operandAt(block); + operand.setDef(top); + + } else if (succPhi instanceof PhiReturnStmt) { + PhiReturnStmt f = (PhiReturnStmt) succPhi; + VarExpr operand = (VarExpr) f.operand(); + operand.setDef(top); + } + + // Adjust the operands of any PhiCatchStmts if the sucessor node + // is protected. + if (top instanceof LocalExpr) { + addCatchPhiOperands(info, succ, (LocalExpr) top); + } + } + + Iterator children = cfg.domChildren(block).iterator(); + + // Visit the children in the dominator tree. Keep the same most + // recent SSA variable (top). + while (children.hasNext()) { + Block child = (Block) children.next(); + search(cfg, info, top, child); + } + } + + /** + * Iterates over the blocks in the CFG and inserts the phi statement + * associated with that block. Up until this point, the phi statement is + * only maintained in SSAConstructionInfo. Note that the phi statement + * cannot be a return phi. + * + * @param cfg + * The CFG into which to insert phi statements. + * @param info + * Represents the variable whose phi statements we are inserting. + * + * @see PhiReturnStmt + */ + private void insertCode(FlowGraph cfg, SSAConstructionInfo info) { + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + PhiStmt phi = info.phiAtBlock(block); + + if (phi != null) { + Assert.isFalse(phi instanceof PhiReturnStmt); + block.tree().prependStmt(phi); + } + } + } + + public String traceMessage(String dateString) { + return " Transforming to SSA: " + dateString; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } +} diff --git a/src/edu/purdue/cs/bloat/ssa/SSAConstructionInfo.java b/src/edu/purdue/cs/bloat/ssa/SSAConstructionInfo.java new file mode 100644 index 0000000..dc4151c --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/SSAConstructionInfo.java @@ -0,0 +1,296 @@ +/* + * Class: SSAConstructionInfo + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.ssa; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Subroutine; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; + +/** + * SSAConstructionInfo contains information needed to convert a CFG + * into SSA form. Each variable (VarExpr) has an SSAConstructionInfo associated + * with it. Each SSAConstructionInfo keeps track of information such + * as the PhiStmts that define copies of the variable, the + * Blocks in which the variable is defined, and the occurrences + * (uses) of the variable in both phi and non-phi statements. Note that no + * PhiStmt is really inserted into a basic block. We just keep track + * of the mapping. It should also be noted that once a phi statement for a given + * variable is "inserted" into a block, no other phi statement for that variable + * is inserted. Thus, the order of insertion determines the precedence of the + * phi statements: PhiReturnStmt > PhiCatchStmt > + * PhiJoinStmt. + * + *

+ * + * Additionally, SSAConstruction has methods to insert various + * flavors of PhiStmts whose targets are the variable associated + * with the SSAConstruction into Blocks. + * + * @see SSA + * @see PhiStmt + * @see PhiCatchStmt + * @see PhiJoinStmt + * @see PhiReturnStmt + */ +public class SSAConstructionInfo { + FlowGraph cfg; // The cfg we're converting into SSA form + + VarExpr prototype; // The variable we're converting into SSA form + + LinkedList[] reals; // The real (non-phi) occurrences associated + + // with a given node (block) + LinkedList allReals; // All the real occurrences of the variable + + PhiStmt[] phis; // Phi statement associated with a given block + + Set defBlocks; // Blocks in which variable is defined + + /** + * Constructor. + * + * @param cfg + * The control flow graph that is being converted to SSA form. + * @param expr + * A variable in the CFG on which SSA analysis is being done. + */ + public SSAConstructionInfo(FlowGraph cfg, VarExpr expr) { + this.cfg = cfg; + + prototype = (VarExpr) expr.clone(); + prototype.setDef(null); + + reals = new LinkedList[cfg.size()]; + allReals = new LinkedList(); + + defBlocks = new LinkedHashSet(); + + phis = new PhiStmt[cfg.size()]; + } + + /** + * Returns the program variable associated with this + * SSAConstructionInfo. + */ + public VarExpr prototype() { + return prototype; + } + + /** + * Makes note of a Block in which the variable is defined by a + * PhiStmt. + */ + public void addDefBlock(Block block) { + defBlocks.add(block); + } + + /** + * Returns the phi statement for the variable represented by this + * SSAConstructionInfo at a given block in the CFG. + */ + public PhiStmt phiAtBlock(Block block) { + return phis[cfg.preOrderIndex(block)]; + } + + /** + * Removes the phi statement for this variable at a given block. + */ + public void removePhiAtBlock(Block block) { + PhiStmt phi = phis[cfg.preOrderIndex(block)]; + + if (phi != null) { + if (SSA.DEBUG) { + System.out.println(" removing " + phi + " at " + block); + } + + phi.cleanup(); + phis[cfg.preOrderIndex(block)] = null; + } + } + + /** + * Adds a PhiJoinStmt for the variable represented by this + * SSAConstructionInfo to a given Block. + */ + public void addPhi(Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + VarExpr target = (VarExpr) prototype.clone(); + + PhiJoinStmt phi = new PhiJoinStmt(target, block); + phis[cfg.preOrderIndex(block)] = phi; + + if (SSA.DEBUG) { + System.out.println(" place " + phi + " in " + block); + } + } + + /** + * Adds a PhiReturnStmt to all of the Blocks that are + * executed upon returning from a given Subroutine. + * + * @see PhiReturnStmt + * @see Subroutine#paths + */ + public void addRetPhis(Subroutine sub) { + Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + Block[] path = (Block[]) paths.next(); + addRetPhi(sub, path[1]); + } + } + + /** + * Inserts a PhiCatchStmt (whose target is the variable + * represented by this SSAConstructionInfo) into a given + * Block. + * + * @see PhiCatchStmt + */ + public void addCatchPhi(Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + if (prototype instanceof LocalExpr) { + LocalExpr target = (LocalExpr) prototype.clone(); + + PhiCatchStmt phi = new PhiCatchStmt(target); + phis[cfg.preOrderIndex(block)] = phi; + + if (SSA.DEBUG) { + System.out.println(" place " + phi + " in " + block); + } + } + } + + /** + * Adds a PhiReturnStmt associated with a given + * Subroutine. The PhiReturnStmt is placed in a given + * block. + * + * @see PhiReturnStmt + */ + private void addRetPhi(Subroutine sub, Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + VarExpr target = (VarExpr) prototype.clone(); + + PhiReturnStmt phi = new PhiReturnStmt(target, sub); + phis[cfg.preOrderIndex(block)] = phi; + + if (SSA.DEBUG) { + System.out.println(" place " + phi + " in " + block); + } + } + + /** + * Notes a real occurrence (that is, a use that is not an operand to a phi + * statement) of the variable represented by this + * SSAConstructionInfo. + * + * @see PhiStmt + */ + public void addReal(VarExpr real) { + if (real.stmt() instanceof PhiStmt) { + return; + } + + Block block = real.block(); + + if (real.isDef()) { + defBlocks.add(block); + } + + Assert.isTrue(block != null, real + " not in a " + block); + + LinkedList l = reals[cfg.preOrderIndex(block)]; + + if (l == null) { + l = new LinkedList(); + reals[cfg.preOrderIndex(block)] = l; + } + + l.add(real); + allReals.add(real); + } + + /** + * Returns all of the real occurrences of this variable. + */ + public Collection reals() { + return allReals; + } + + /** + * Returns all of the real occurrences of this variable in a given block. + */ + public Collection realsAtBlock(Block block) { + LinkedList l = reals[cfg.preOrderIndex(block)]; + + if (l == null) { + l = new LinkedList(); + reals[cfg.preOrderIndex(block)] = l; + } + + return l; + } + + /** + * Returns the Blocks containing a definition of the variable represented by + * this SSAConstruction info. + */ + public Collection defBlocks() { + return defBlocks; + } +} diff --git a/src/edu/purdue/cs/bloat/ssa/SSAGraph.java b/src/edu/purdue/cs/bloat/ssa/SSAGraph.java new file mode 100644 index 0000000..8750ee6 --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/SSAGraph.java @@ -0,0 +1,731 @@ +/* + * Class: SSAGraph + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.ssa; + +import java.util.*; +import java.io.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.tree.CheckExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; + +/** + * The SSA graph (also called the value graph) represents the nesting of + * expression in a control flow graph. Each node in the SSA graph represents an + * expression. If the expression is a definition, the it is labeled with the + * variable it defines. Each node has directed edges to the nodes representing + * its operands. + * + *

+ * + * SSAGraph is a representation of the definitions found in a CFG in + * the following form: Each node in the graph is an expression that defines a + * variable (a VarExpr, PhiStmt, or a + * StackManipStmt). Edges in the graph point to the nodes whose + * expressions define the operands of the expression in the source node. + * + *

+ * + * This class is used primarily get the strongly connected components of the SSA + * graph in support of value numbering and induction variable analysis. + * + *

+ * + * Nate warns: Do not modify the CFG while using the SSA graph! The effects of + * such modification are undefined and will probably lead to nasty things + * occuring. + * + * @see edu.purdue.cs.bloat.trans.ValueNumbering ValueNumbering + */ +public class SSAGraph { + public static boolean DEBUG = false; + + FlowGraph cfg; + + LinkedHashMap equiv; // A mapping between a Node and all its equivalent + // Nodes + + /** + * Grumble. + */ + public SSAGraph(FlowGraph cfg, boolean useless) { + this(cfg); + } + + /** + * Constructor. Traverse the control flow graph and determines which Nodes + * are of an equivalent Type. + * + * @param cfg + * The control flow graph to examine + */ + public SSAGraph(FlowGraph cfg) { + this.cfg = cfg; + this.equiv = new LinkedHashMap(); + + cfg.visit(new TreeVisitor() { + // The CheckExpr and the Expr is checks are equivalent. + public void visitCheckExpr(CheckExpr expr) { + expr.visitChildren(this); + makeEquiv(expr, expr.expr()); + } + + // The target of the PhiStmt and the PhiStmt are equivalent + public void visitPhiStmt(PhiStmt stmt) { + stmt.visitChildren(this); + makeEquiv(stmt.target(), stmt); + } + + // The use of a variable and its defining variable are + // equivalent + public void visitVarExpr(VarExpr expr) { + if (!expr.isDef()) { + VarExpr def = (VarExpr) expr.def(); + + if (def != null) { + makeEquiv(expr, def); + } + } + } + + // With StackManipStmts the stack slot (StackExpr) after the + // StackManipStmt is equivalent to its corresponding slot before + // the StackManipStmt. + public void visitStackManipStmt(StackManipStmt stmt) { + StackExpr[] target = stmt.target(); + StackExpr[] source = stmt.source(); + + switch (stmt.kind()) { + case StackManipStmt.SWAP: + // 0 1 -> 1 0 + Assert.isTrue(source.length == 2 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0 }); + break; + case StackManipStmt.DUP: + // 0 -> 0 0 + Assert.isTrue(source.length == 1 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 0, 0 }); + break; + case StackManipStmt.DUP_X1: + // 0 1 -> 1 0 1 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + break; + case StackManipStmt.DUP_X2: + if (source.length == 3) { + // 0 1 2 -> 2 0 1 2 + Assert.isTrue(source.length == 3 && target.length == 4, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 2, 0, 1, 2 }); + } else { + // 0-1 2 -> 2 0-1 2 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + case StackManipStmt.DUP2: + if (source.length == 2) { + // 0 1 -> 0 1 0 1 + Assert.isTrue(target.length == 4, "Illegal statement: " + + stmt); + manip(source, target, new int[] { 0, 1, 0, 1 }); + } else { + // 0-1 -> 0-1 0-1 + Assert.isTrue(source.length == 1 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 0, 0 }); + } + break; + case StackManipStmt.DUP2_X1: + if (source.length == 3) { + // 0 1 2 -> 1 2 0 1 2 + Assert.isTrue(target.length == 5, "Illegal statement: " + + stmt); + manip(source, target, new int[] { 1, 2, 0, 1, 2 }); + } else { + // 0 1-2 -> 1-2 0 1-2 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + case StackManipStmt.DUP2_X2: + if (source.length == 4) { + // 0 1 2 3 -> 2 3 0 1 2 3 + Assert.isTrue(target.length == 6, "Illegal statement: " + + stmt); + manip(source, target, new int[] { 2, 3, 0, 1, 2, 3 }); + } else if (source.length == 3) { + if (target.length == 5) { + // 0-1 2 3 -> 2 3 0-1 2 3 + manip(source, target, new int[] { 1, 2, 0, 1, 2 }); + } else { + // 0 1 2-3 -> 2-3 0 1 2-3 + Assert.isTrue(target.length == 4, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 2, 0, 1, 2 }); + } + } else { + // 0-1 2-3 -> 2-3 0-1 2-3 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + } + + stmt.visitChildren(this); + } + + // Determines equivalence of the StackExprs invovled in a + // StackManipStmt. Recall that StackManipStmt are things like + // the dup and swap instructions. So, elements (StackExprs) of + // the "new" stack will be equivalent to elements of the "old" + // stack. The s array defines the transformation. + private void manip(StackExpr[] source, StackExpr[] target, int[] s) { + for (int i = 0; i < s.length; i++) { + makeEquiv(target[i], source[s[i]]); + } + } + + // The StoreExpr is equivalent to the expression being stored. + public void visitStoreExpr(StoreExpr expr) { + expr.visitChildren(this); + makeEquiv(expr, expr.expr()); + + if (expr.target() instanceof VarExpr) { + makeEquiv(expr.target(), expr.expr()); + } + } + }); + } + + /** + * Returns the FlowGraph that this SSAGraph is built + * around. + */ + public FlowGraph cfg() { + return (this.cfg); + } + + /** + * Returns a set of nodes whose value is equivalent to a given node. For + * example, the LHS and RHS of an assignment are equivalent. As are all + * local variables with the same definition. + */ + public Set equivalent(Node node) { + Set s = (Set) equiv.get(node); + + if (s == null) { + s = new LinkedHashSet(1); + s.add(node); // A node is equivalent to itself + equiv.put(node, s); + } + + return s; + } + + /** + * Makes node1 equivalent to node2 by adding the equivlance Set of node2 to + * the equivalance Set of node1, and vice versa. + */ + void makeEquiv(Node node1, Node node2) { + Set s1 = equivalent(node1); + Set s2 = equivalent(node2); + + if (s1 != s2) { + s1.addAll(s2); + + Iterator iter = s2.iterator(); + + while (iter.hasNext()) { + Node n = (Node) iter.next(); + equiv.put(n, s1); + } + } + } + + /** + * Returns the children (that is, the operands) of a given Node in the SSA + * Graph. + */ + public List children(Node node) { + final ArrayList c = new ArrayList(); + + if (node instanceof StoreExpr) { + StoreExpr store = (StoreExpr) node; + + // Add the grand children of RHS. The RHS is equivalent to + // this node. + store.expr().visitChildren(new TreeVisitor() { + public void visitNode(Node node) { + c.add(node); + } + }); + + // The LHS is equivalent to this node if it is a VarExpr and not + // a child. + if (!(store.target() instanceof VarExpr)) { + c.add(store.target()); + } + + } else if (node instanceof PhiStmt) { + PhiStmt phi = (PhiStmt) node; + c.addAll(phi.operands()); + + } else { + node.visitChildren(new TreeVisitor() { + public void visitNode(Node node) { + c.add(node); + } + }); + } + + return c; + } + + /** + * Returns the Sets of Nodes whose values are equivalent. + */ + public Collection equivalences() { + return equiv.values(); + } + + class Count { + int value = 0; + } + + /** + * Calculates the strongly connected components (SCC) of the SSA graph. SSCs + * are represented by a List of Nodes. The SCCs are then visited + * by the ComponentVistor. + */ + public void visitComponents(final ComponentVisitor visitor) { + // Number the nodes reverse post order (i.e. topological order). + + final Count count = new Count(); + + final List postOrder = cfg.postOrder(); + ListIterator iter = postOrder.listIterator(postOrder.size()); + + // Perform a depth-first ordering of the nodes in the CFG to give + // each node a unique identifier. This is accomplished by + // visiting the blocks in the CFG in post-order and numbering the + // Nodes in the block's expression Tree in depth-first order. + while (iter.hasPrevious()) { + Block block = (Block) iter.previous(); + + block.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(Node node) { + node.visitChildren(this); + node.setKey(count.value++); + } + }); + } + + // Build the (strongly connected) components and call + // visitor.visitComponent for each. + cfg.visit(new TreeVisitor() { + ArrayList stack = new ArrayList(); + + BitSet onStack = new BitSet(count.value + 1); + + int[] low = new int[count.value + 1]; + + int[] dfs = new int[count.value + 1]; + + int dfsNumber = 1; + + Node parent; // Parent in the SSA graph + + // Visit the blocks in the CFG in reverse postorder + public void visitFlowGraph(FlowGraph cfg) { + ListIterator e = postOrder.listIterator(postOrder.size()); + + while (e.hasPrevious()) { + Block block = (Block) e.previous(); + block.visit(this); + } + } + + public void visitTree(Tree tree) { + parent = null; + tree.visitChildren(this); + } + + // This method is essentially Figure 4.6 in Taylor Simpson's PhD + // Thesis: www.cs.rice.edu/~lts. The implementation is a little + // funky, though, because someone wanted to use visitors. + // Grumble. + public void visitNode(Node node) { + int dfn = dfs[node.key()]; + // System.out.println("visit " + node + " key=" + node.key() + + // " dfn=" + dfn); + + if (dfn == 0) { + // The node in question has not yet been visited. Assign + // it + // the next dfNumber and add it to the stack. Mark all + // nodes that are equivalent to the node in question as + // being visited. + + dfn = dfsNumber++; + low[dfn] = dfn; + + stack.add(node); + onStack.set(dfn); + + Iterator equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + Node e = (Node) equiv.next(); + dfs[e.key()] = dfn; + } + + // Again examine each node, e, equivalent to the node in + // question. Then recursively visit the children of e in + // the SSA Graph. + Node grandParent = parent; + parent = node; + + equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + Node e = (Node) equiv.next(); + + Iterator children = children(e).iterator(); + + while (children.hasNext()) { + Node child = (Node) children.next(); + child.visit(this); + } + } + + parent = grandParent; // Restore true parent + + // Now we finally get to the point where we can + // construct a + // strongly connected component. Pop all of the nodes + // off + // the stack until the node in question is reached. + if (low[dfn] == dfn) { + ArrayList scc = new ArrayList(); + + while (!stack.isEmpty()) { + Node v = (Node) stack.remove(stack.size() - 1); + onStack.clear(dfs[v.key()]); + scc.addAll(equivalent(v)); + + if (v == node) { + break; + } + } + + // Sort the nodes in the SCC by their reverse + // post order numbers. + Collections.sort(scc, new Comparator() { + public int compare(Object a, Object b) { + int ka = ((Node) a).key(); + int kb = ((Node) b).key(); + return ka - kb; + } + }); + + if (DEBUG) { + System.out.print("SCC ="); + + Iterator e = scc.iterator(); + + while (e.hasNext()) { + Node v = (Node) e.next(); + System.out.print(" " + v + "{" + v.key() + "}"); + } + + System.out.println(); + } + + // Visit the SCC with the visitor that was passed in. + visitor.visitComponent(scc); + } + + if (parent != null) { + int parentDfn = dfs[parent.key()]; + low[parentDfn] = Math.min(low[parentDfn], low[dfn]); + } + + } else { + // We've already visited the node in question + if (parent != null) { + int parentDfn = dfs[parent.key()]; + + // (parent, node) is either a cross edge or a back edge. + if (dfn < parentDfn && onStack.get(dfn)) { + low[parentDfn] = Math.min(low[parentDfn], dfn); + } + } + } + } + }); + } + + /** + * Visits the strongly connected component that contains a given + * Node. + */ + public void visitComponent(final Node startNode, + final ComponentVisitor visitor) { + // Number the nodes reverse post order (i.e. topological order). + + final Count count = new Count(); + + final List postOrder = cfg.postOrder(); + ListIterator iter = postOrder.listIterator(postOrder.size()); + + // Perform a depth-first ordering of the nodes in the CFG to give + // each node a unique identifier. This is accomplished by + // visiting the blocks in the CFG in post-order and numbering the + // Nodes in the block's expression Tree in depth-first order. + while (iter.hasPrevious()) { + Block block = (Block) iter.previous(); + + block.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(Node node) { + node.visitChildren(this); + node.setKey(count.value++); + } + }); + } + + // Build the (strongly connected) components and call + // visitor.visitComponent for each. + cfg.visit(new TreeVisitor() { + ArrayList stack = new ArrayList(); + + BitSet onStack = new BitSet(count.value + 1); + + int[] low = new int[count.value + 1]; + + int[] dfs = new int[count.value + 1]; + + int dfsNumber = 1; + + Node parent; // Parent in the SSA graph + + // Visit the blocks in the CFG in reverse postorder + public void visitFlowGraph(FlowGraph cfg) { + ListIterator e = postOrder.listIterator(postOrder.size()); + + while (e.hasPrevious()) { + Block block = (Block) e.previous(); + block.visit(this); + } + } + + public void visitTree(Tree tree) { + parent = null; + tree.visitChildren(this); + } + + // This method is essentially Figure 4.6 in Taylor Simpson's PhD + // Thesis: www.cs.rice.edu/~lts. The implementation is a little + // funky, though, because someone wanted to use visitors. + // Grumble. + public void visitNode(Node node) { + int dfn = dfs[node.key()]; + // System.out.println("visit " + node + " key=" + node.key() + + // " dfn=" + dfn); + + if (dfn == 0) { + // If this node isn't equivalent to the node the care + // about, + // fergit it! + if (!equivalent(node).contains(startNode)) + return; + + // The node in question has not yet been visited. Assign + // it + // the next dfNumber and add it to the stack. Mark all + // nodes that are equivalent to the node in question as + // being visited. + + dfn = dfsNumber++; + low[dfn] = dfn; + + stack.add(node); + onStack.set(dfn); + + Iterator equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + Node e = (Node) equiv.next(); + dfs[e.key()] = dfn; + } + + // Again examine each node, e, equivalent to the node in + // question. Then recursively visit the children of e in + // the SSA Graph. + Node grandParent = parent; + parent = node; + + equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + Node e = (Node) equiv.next(); + + Iterator children = children(e).iterator(); + + while (children.hasNext()) { + Node child = (Node) children.next(); + child.visit(this); + } + } + + parent = grandParent; // Restore true parent + + // Now we finally get to the point where we can + // construct a + // strongly connected component. Pop all of the nodes + // off + // the stack until the node in question is reached. + if (low[dfn] == dfn) { + ArrayList scc = new ArrayList(); + + while (!stack.isEmpty()) { + Node v = (Node) stack.remove(stack.size() - 1); + onStack.clear(dfs[v.key()]); + scc.addAll(equivalent(v)); + + if (v == node) { + break; + } + } + + // Sort the nodes in the SCC by their reverse + // post order numbers. + Collections.sort(scc, new Comparator() { + public int compare(Object a, Object b) { + int ka = ((Node) a).key(); + int kb = ((Node) b).key(); + return ka - kb; + } + }); + + if (DEBUG) { + System.out.print("SCC ="); + + Iterator e = scc.iterator(); + + while (e.hasNext()) { + Node v = (Node) e.next(); + System.out.print(" " + v + "{" + v.key() + "}"); + } + + System.out.println(); + } + + // Visit the SCC with the visitor that was passed in. + visitor.visitComponent(scc); + } + + if (parent != null) { + int parentDfn = dfs[parent.key()]; + low[parentDfn] = Math.min(low[parentDfn], low[dfn]); + } + + } else { + // We've already visited the node in question + if (parent != null) { + int parentDfn = dfs[parent.key()]; + + // (parent, node) is either a cross edge or a back edge. + if (dfn < parentDfn && onStack.get(dfn)) { + low[parentDfn] = Math.min(low[parentDfn], dfn); + } + } + } + } + }); + } + + /** + * Prints a textual representation of the strongly connected components of + * the SSAGraph to a PrintWriter. + */ + public void printSCCs(PrintWriter pw) { + Collection equivs = this.equivalences(); // A Collection of Sets + Iterator iter = equivs.iterator(); + + pw.println("Strongly Connected Components of the SSAGraph"); + + for (int i = 1; iter.hasNext(); i++) { + Set scc = (Set) iter.next(); + Iterator sccIter = scc.iterator(); + + pw.println(" Component " + i); + + while (sccIter.hasNext()) { + Node node = (Node) sccIter.next(); + pw.println(" " + node + " [VN = " + node.valueNumber() + + ", ID = " + System.identityHashCode(node) + "]"); + } + } + } +} diff --git a/src/edu/purdue/cs/bloat/ssa/package.html b/src/edu/purdue/cs/bloat/ssa/package.html new file mode 100644 index 0000000..d4e82a2 --- /dev/null +++ b/src/edu/purdue/cs/bloat/ssa/package.html @@ -0,0 +1,12 @@ + + + +

Converts a control flow graph into static single assignment (SSA) +form. In SSA form, each variable in the method has a number +associated with it. Each target of an assignment statement is +assigned a new number. Subsequent uses of that variable are assigned +the same number. Thus, we have a compact form of definition-use +information. Many of the optimiations we do take advantage of SSA form.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/tbaa/Makefile b/src/edu/purdue/cs/bloat/tbaa/Makefile new file mode 100644 index 0000000..90a7672 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tbaa/Makefile @@ -0,0 +1,26 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + TBAA.class\ + TypeInference.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/tbaa/TBAA.java b/src/edu/purdue/cs/bloat/tbaa/TBAA.java new file mode 100644 index 0000000..541f0e1 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tbaa/TBAA.java @@ -0,0 +1,296 @@ +/* + * Class: TBAA + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tbaa; + +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.*; + +import edu.purdue.cs.bloat.editor.ClassHierarchy; +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.MemRefExpr; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.util.Assert; + +import java.util.*; + +/** + * Performs Type-Based Alias Analysis (TBAA) to determine if one expression can + * alias another. An expression may alias another expression if there is the + * possibility that they refer to the same location in memory. BLOAT models + * expressions that may reference memory locations with MemRefExprs. + * There are two kinds of "access expressions" in Java: field accesses (FieldExpr + * and StaticFieldExpr) and array references (ArrayRefExpr). + * Access paths consist of one or more access expressions. For instance, + * a.b[i].c is an access expression. + * + *

+ * + * TBAA uses the FieldTypeDecl relation to determine whether or not two access + * paths may refer to the same memory location. + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
+ * AP1 AP2 FieldTypeDecl(AP1, Ap2)
p p true
p.f q.g (f = g) && FieldTypeDecl(p, q)
p.f q[i]false
p[i]q[j]FieldTypeDecl(p, q)
p q TypeDecl(p, q)
+ * + *

+ * + * The TypeDecl(AP1, AP2) relation is defined as: + *

+ * Subtypes(Type(AP1)) INTERSECT Subtypes(Type(AP2)) != EMPTY + *

+ * + * The subtype relationships are determined by the ClassHierarchy. + * + * @see ClassHierarchy + * @see MemRefExpr + * @see FieldExpr + * @see StaticFieldExpr + * @see ArrayRefExpr + */ +public class TBAA { + + /** + * Returns true, if expression a can alias expression b. + */ + public static boolean canAlias(EditorContext context, Expr a, Expr b) { + // Only memory reference expressions can have aliases. + if (!(a instanceof MemRefExpr)) { + return false; + } + + // Only memory reference expressions can have aliases. + if (!(b instanceof MemRefExpr)) { + return false; + } + + // Equal expressions can be aliases. + if (a.equalsExpr(b)) { + return true; + } + + MemberRef af = null; // Field accessed by expression a + MemberRef bf = null; // Field accessed by expression b + + if (a instanceof FieldExpr) { + af = ((FieldExpr) a).field(); + } + + if (a instanceof StaticFieldExpr) { + af = ((StaticFieldExpr) a).field(); + } + + if (b instanceof FieldExpr) { + bf = ((FieldExpr) b).field(); + } + + if (b instanceof StaticFieldExpr) { + bf = ((StaticFieldExpr) b).field(); + } + + // Arrays and fields cannot alias the same location. + if (a instanceof ArrayRefExpr && bf != null) { + return false; + } + + // Arrays and fields cannot alias the same location. + if (b instanceof ArrayRefExpr && af != null) { + return false; + } + + Collection types = new LinkedHashSet(); + types.add(a.type()); + types.add(b.type()); + ClassHierarchy hier = new ClassHierarchy(context, types, true); + + // Only type-compatible arrays can alias the same location. + if (a instanceof ArrayRefExpr && b instanceof ArrayRefExpr) { + ArrayRefExpr aa = (ArrayRefExpr) a; + ArrayRefExpr bb = (ArrayRefExpr) b; + + Type aaIndexType = aa.index().type(); + Type bbIndexType = bb.index().type(); + Type aaArrayType = aa.array().type(); + Type bbArrayType = bb.array().type(); + + Assert.isTrue(Assert.isIntegral(aaIndexType), aa.index() + " in " + + aa + " (" + aaIndexType + ") is not an integer"); + Assert.isTrue(Assert.isIntegral(bbIndexType), bb.index() + " in " + + bb + " (" + bbIndexType + ") is not an integer"); + Assert.isTrue(aaArrayType instanceof ArrayType + || aaArrayType.equals(Type.OBJECT) + || aaArrayType.equals(new ObjectType( + "java.lang.Serializable")) + || aaArrayType.equals(new ObjectType("jav.lang.Cloneable")) + || aaArrayType.equals(Type.NULL), aa.array() + " in " + aa + + " (" + aaArrayType + ") is not an array"); + Assert.isTrue(bbArrayType instanceof ArrayType + || bbArrayType.equals(Type.OBJECT) + || bbArrayType.equals(new ObjectType( + "java.lang.Serializable")) + || bbArrayType.equals(new ObjectType("jav.lang.Cloneable")) + || bbArrayType.equals(Type.NULL), bb.array() + " in " + bb + + " (" + bbArrayType + ") is not an array"); + + // Optimization: if constant indices. Only equal indices can + // alias the same location. + if (aa.index() instanceof ConstantExpr + && bb.index() instanceof ConstantExpr) { + + ConstantExpr ai = (ConstantExpr) aa.index(); + ConstantExpr bi = (ConstantExpr) bb.index(); + + if (ai.value() != null && bi.value() != null) { + if (!ai.value().equals(bi.value())) { + return false; + } + } + } + + return intersects(hier, aaArrayType, bbArrayType); + } + + try { + if (af != null) { + ClassGen klass = context.editClass(((ObjectType) af + .declaringClass()).getClassName()); + Field e = klass.containsField(af.name()); + if (e == null) { + throw new NoSuchFieldException("Field: " + af.name() + + "not found in class " + af.declaringClass()); + } + + if (e.isVolatile()) { + // context.release(e.fieldInfo()); + return true; + } + + if (e.isFinal()) { + // context.release(e.fieldInfo()); + return false; + } + + // context.release(e.fieldInfo()); + // WE NEVER STARTED EDITING SO DON'T HAVE TO RELEASE IT + } + + if (bf != null) { + ClassGen klass = context.editClass(((ObjectType) bf + .declaringClass()).getClassName()); + Field e = klass.containsField(bf.name()); + if (e == null) { + throw new NoSuchFieldException("Field: " + bf.name() + + "not found in class " + bf.declaringClass()); + } + + if (e.isVolatile()) { + // context.release(e.fieldInfo()); + return true; + } + + if (e.isFinal()) { + // context.release(e.fieldInfo()); + return false; + } + + // context.release(e.fieldInfo()); + // WE NEVER STARTED EDITING SO DON'T HAVE TO RELEASE IT + } + + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume there is an alias. + return true; + } catch (ClassNotFoundException cnfe) { + // A field wasn't found. Silently assume there is an alias. + return true; + } + + // Only fields with the same name can alias the same location. + if (af != null && bf != null) { + return af.equals(bf); + } + + // Default case. This shouldn't happen. + return intersects(hier, a.type(), b.type()); + } + + /** + * Returns true if type a and type c intersect. That is, the two + * types have a non-null intersection. + */ + private static boolean intersects(ClassHierarchy hier, Type a, Type b) { + return !hier.intersectType(a, b).equals(Type.NULL); + } +} diff --git a/src/edu/purdue/cs/bloat/tbaa/TypeInference.java b/src/edu/purdue/cs/bloat/tbaa/TypeInference.java new file mode 100644 index 0000000..87b639a --- /dev/null +++ b/src/edu/purdue/cs/bloat/tbaa/TypeInference.java @@ -0,0 +1,829 @@ +/* + * Class: TypeInference + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tbaa; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.context.BloatContext; +import edu.purdue.cs.bloat.editor.ClassHierarchy; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.ssa.ComponentVisitor; +import edu.purdue.cs.bloat.ssa.SSAGraph; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallMethodExpr; +import edu.purdue.cs.bloat.tree.CallStaticExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.CheckExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.InstanceOfExpr; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.NegExpr; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.ReturnAddressExpr; +import edu.purdue.cs.bloat.tree.ShiftExpr; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; + +import java.util.*; + +/** + * Typed-based alias analysis requires that we know the types of all entities in + * a method. However, local variables and stack variables do not have declared + * types. Thus, we have to infer them. + * + *

+ * + * TypeInference uses a simpilified version of everyone's favorite + * type inference algorithm from Object-Oriented Type Systems by + * Palsberg and Schwartzbach. It is simplified in that no interprocedural type + * information is calculated. + * + *

+ * + * Here's how it works. Entities such as method arguments, fields, and constants + * have a known type. This known type is expressed in a constraint that + * constraints the entity to its type. Operations involving assignment (also + * phi-statements and stack operations) propagate these constraints to the + * targets of the assignments. + */ +public class TypeInference implements Optimization { + public static boolean DEBUG = false; + + static final Type UNDEF = Type.getType("Lundef!;"); + + public void transform(MethodState state) { + transform(state.controlFlowGraph(), state.context()); + } + + /** + * Traverses the control flow graph and with the help of a number of vistors + * performs type-based alias analysis. + * + * @param cfg + * The control flow graph on which to perform TBAA. + * @param context + * Used to obtain a class heirarchy used for getting information + * about Types (classes). + * + * @see SSAGraph + * @see ComponentVisitor + * @see TreeVisitor + * + */ + public void transform(FlowGraph cfg, BloatContext context) { + ClassHierarchy hier = context.getHierarchy(); + + // First go through the CFG and determine the type of the local + // variables corresponding to method arguments. Assign those + // types to all uses of those local variables. + cfg.visit(new TreeVisitor() { + public void visitInitStmt(InitStmt stmt) { + // a := INIT + // [declared param type] <= [a] + + MethodGen method = stmt.block().graph().method(); + + LocalExpr[] t = stmt.targets(); + + for (int i = 0; i < t.length; i++) { + LocalExpr var = t[i]; + + int index = var.index(); + + // If the method is static, there is no this pointer in + // the + // local variable table, so decrement index to point to + // the + // indexth local variable. This is needed with indexing + // into method.type().indexedParamTypes(). + if (!method.isStatic()) { + index--; + } + + Type type; + + if (index == -1) { + // If the index is -1, then we're dealing with local + // variable 0 of a non-static method. This local + // variable + // is the this pointer. Its type is the type of the + // class + // in which the method is declared. + type = new ObjectType(method.getClassName()); + + } else { + // One of the method's arguments is being initialized. + // Figure out its type. + type = method.getArgumentType(index); + } + + var.setType(type); + + Iterator uses = var.uses().iterator(); + + // Set the type of the uses of the local variable, also. + while (uses.hasNext()) { + LocalExpr use = (LocalExpr) uses.next(); + use.setType(type); + } + } + } + + public void visitExpr(Expr expr) { + expr.visitChildren(this); + + // We don't know the type of the expression yet. + expr.setType(UNDEF); + } + }); + + SSAGraph ssaGraph = new SSAGraph(cfg); + + final TypeInferenceVisitor visitor = new TypeInferenceVisitor(hier, + ssaGraph); + + // Visit each strong connected component in the SSAGraph and + // compute the type information using the TypeInferenceVisitor. + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(List scc) { + visitor.changed = true; + + while (visitor.changed) { + visitor.changed = false; + + Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + Node node = (Node) iter.next(); + node.visit(visitor); + } + } + } + }); + + // Convert POS_SHORT types back into SHORT types and POS_BYTE + // types back into BYTE types. + cfg.visit(new TreeVisitor() { + public void visitExpr(Expr expr) { + expr.visitChildren(this); + + if (expr.type().equals(ClassHierarchy.POS_SHORT)) { + expr.setType(Type.SHORT); + + } else if (expr.type().equals(ClassHierarchy.POS_BYTE)) { + expr.setType(Type.BYTE); + } + } + }); + + if (DEBUG) { + // Print out all the type information and do some checking. + cfg.visit(new TreeVisitor() { + public void visitExpr(Expr expr) { + expr.visitChildren(this); + + System.out.println("typeof(" + expr + ") = " + expr.type()); + + if (expr.type().equals(UNDEF)) { + System.out.println("WARNING: typeof(" + expr + + ") = UNDEF"); + } + + Assert + .isFalse(expr.type().equals( + ClassHierarchy.POS_SHORT)); + Assert.isFalse(expr.type().equals(ClassHierarchy.POS_BYTE)); + } + }); + } + } + + public String preDebugMessage() { + return "---------Doing type inference--------"; + } + + public String traceMessage(String dateString) { + return " Type Inferencing: " + dateString; + } + + public String postDebugMessage() { + return null; + } +} + +/** + * Does most of the type inference work. It generates the type constraints for + * the expressions in the CFG. + */ +class TypeInferenceVisitor extends TreeVisitor { + public boolean changed; + + public ClassHierarchy hier; + + SSAGraph ssaGraph; + + public TypeInferenceVisitor(ClassHierarchy hier, SSAGraph ssaGraph) { + this.hier = hier; + this.ssaGraph = ssaGraph; + } + + public void visitExpr(Expr expr) { + throw new RuntimeException(expr + " not supported"); + } + + // Make a new start constraint for the expression being shifted. + public void visitShiftExpr(ShiftExpr expr) { + if (Assert.isIntegral(expr.expr().type()) + || expr.expr().type().equals(ClassHierarchy.POS_SHORT) + || expr.expr().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INT); + + } else { + prop(expr, expr.expr()); + } + } + + // If either of the ArithExpr's operands has type INTEGER, then the + // entire expression must have type INTEGER. Otherwise, the type of + // one of the operands must be undefined, or both operands have the + // same type. If the ArithExpr is one of the compare operations, + // then it has an INTEGER type. + public void visitArithExpr(ArithExpr expr) { + if (Assert.isIntegral(expr.left().type()) + || expr.left().type().equals(ClassHierarchy.POS_SHORT) + || expr.left().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INT); + + } else if (Assert.isIntegral(expr.right().type()) + || expr.right().type().equals(ClassHierarchy.POS_SHORT) + || expr.right().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INT); + + } else { + Assert.isTrue(expr.left().type().equals(TypeInference.UNDEF) + || expr.right().type().equals(TypeInference.UNDEF) + || expr.left().type().equals(expr.right().type()), expr + .left() + + ".type() = " + + expr.left().type() + + " != " + + expr.right() + + ".type() = " + expr.right().type()); + + if (expr.operation() == ArithExpr.CMP + || expr.operation() == ArithExpr.CMPL + || expr.operation() == ArithExpr.CMPG) { + + start(expr, Type.INT); + + } else { + prop(expr, expr.left()); + } + } + } + + // If the expression being negated has an integral type, then the + // type of the expression in an INTEGER. + public void visitNegExpr(NegExpr expr) { + if (Assert.isIntegral(expr.expr().type()) + || expr.expr().type().equals(ClassHierarchy.POS_SHORT) + || expr.expr().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INT); + + } else { + prop(expr, expr.expr()); + } + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + start(expr, ReturnaddressType.NO_TARGET); + } + + public void visitCheckExpr(CheckExpr expr) { + prop(expr, expr.expr()); + } + + // Why does it start the expression with an INTEGER type??? + // Ans: Because instanceof returns a boolean, which is dealt with as an + // int. + public void visitInstanceOfExpr(InstanceOfExpr expr) { + start(expr, Type.INT); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + start(expr, Type.INT); + } + + // If a VarExpr does not define a variable, then propagate the type + // of the expression that does define the variable to the VarExpr. + public void visitVarExpr(VarExpr expr) { + if (!expr.isDef()) { + if (expr.def() != null) { + prop(expr, expr.def()); + } + } + } + + // Not surprisingly, StackManipStmts are ugly looking. The type + // information is propagated from the "before" stack variable to the + // appropriate "after" stack variable. As seen in other places, the + // transformation is given by an integer array. + public void visitStackManipStmt(StackManipStmt stmt) { + // a := b + // [b] <= [a] + + StackExpr[] target = stmt.target(); + StackExpr[] source = stmt.source(); + + switch (stmt.kind()) { + case StackManipStmt.SWAP: + // 0 1 -> 1 0 + Assert.isTrue(source.length == 2 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0 }); + break; + case StackManipStmt.DUP: + // 0 -> 0 0 + Assert.isTrue(source.length == 1 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 0, 0 }); + break; + case StackManipStmt.DUP_X1: + // 0 1 -> 1 0 1 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + break; + case StackManipStmt.DUP_X2: + if (source.length == 3) { + // 0 1 2 -> 2 0 1 2 + Assert.isTrue(source.length == 3 && target.length == 4, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 2, 0, 1, 2 }); + } else { + // 0-1 2 -> 2 0-1 2 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + case StackManipStmt.DUP2: + if (source.length == 2) { + // 0 1 -> 0 1 0 1 + Assert.isTrue(target.length == 4, "Illegal statement: " + stmt); + manip(source, target, new int[] { 0, 1, 0, 1 }); + } else { + // 0-1 -> 0-1 0-1 + Assert.isTrue(source.length == 1 && target.length == 2, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 0, 0 }); + } + break; + case StackManipStmt.DUP2_X1: + if (source.length == 3) { + // 0 1 2 -> 1 2 0 1 2 + Assert.isTrue(target.length == 5, "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 2, 0, 1, 2 }); + } else { + // 0 1-2 -> 1-2 0 1-2 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + case StackManipStmt.DUP2_X2: + if (source.length == 4) { + // 0 1 2 3 -> 2 3 0 1 2 3 + Assert.isTrue(target.length == 6, "Illegal statement: " + stmt); + manip(source, target, new int[] { 2, 3, 0, 1, 2, 3 }); + } else if (source.length == 3) { + if (target.length == 5) { + // 0-1 2 3 -> 2 3 0-1 2 3 + manip(source, target, new int[] { 1, 2, 0, 1, 2 }); + } else { + // 0 1 2-3 -> 2-3 0 1 2-3 + Assert.isTrue(target.length == 4, "Illegal statement: " + + stmt); + manip(source, target, new int[] { 2, 0, 1, 2 }); + } + } else { + // 0-1 2-3 -> 2-3 0-1 2-3 + Assert.isTrue(source.length == 2 && target.length == 3, + "Illegal statement: " + stmt); + manip(source, target, new int[] { 1, 0, 1 }); + } + break; + } + + stmt.visitChildren(this); + } + + private void manip(StackExpr[] source, StackExpr[] target, int[] s) { + for (int i = 0; i < s.length; i++) { + prop(target[i], source[s[i]]); + } + } + + // The type of the expression being stored flows into the target of + // the store. The type of the target flows into the StoreExpr. + public void visitStoreExpr(StoreExpr expr) { + // a := b + // [b] <= [a] + + prop(expr.target(), expr.expr()); + prop(expr, expr.target()); + } + + // The type of the exception being caught flows into the type of the + // CatchExpr. + public void visitCatchExpr(CatchExpr expr) { + // Catch(t) + // t <= [Catch(t)] + + Type catchType = expr.catchType(); + + if (catchType == Type.NULL) { + catchType = Type.THROWABLE; + } + + start(expr, catchType); + } + + // The type information of each of the PhiStmt's operands flows into + // the target. If an operand is undefined, the type of the target + // flows into that operand so that it will have a type. + public void visitPhiStmt(PhiStmt stmt) { + // a := Phi(b, c) + // [b] <= [a] + // [c] <= [a] + + List back = new ArrayList(stmt.operands().size()); + + Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + Expr expr = (Expr) e.next(); + + if (expr instanceof VarExpr && expr.def() == null) { + back.add(expr); + + } else { + prop(stmt.target(), expr); + } + } + + // Propagate the type back to the operands which are undefined. + // Otherwise, they won't be able to get a type. + e = back.iterator(); + + while (e.hasNext()) { + Expr expr = (Expr) e.next(); + + if (expr.def() == null) { + prop(expr, stmt.target()); + } + } + } + + // If the ArrayRefExpr is not a definition, then the type of the + // elements in the array flows into the type of the ArrayRefExpr. + // This is all contingent upon the type of the array elements being + // defined, non-object, not serializable, not clonable, and not + // null. + public void visitArrayRefExpr(ArrayRefExpr expr) { + // a[i] + // [a[i]] <= [element type of a] + + Expr array = expr.array(); + + if (!expr.isDef()) { + if (!array.type().equals(TypeInference.UNDEF) + && !array.type().equals(Type.OBJECT) + && !array.type().equals( + new ObjectType("java.lang.Serializable")) + && !array.type().equals( + new ObjectType("java.lang.Cloneable")) + && !array.type().equals(Type.NULL)) { + + Assert.isTrue(array.type() instanceof ArrayType, array + " in " + + expr + " (" + array.type() + ") is not an array"); + start(expr, ((ArrayType) expr.array().type()).getElementType()); + } + } + } + + // The return type of the method flow into the CallMethodExpr. + public void visitCallMethodExpr(CallMethodExpr expr) { + // x.m(a), m in class C + // [x] <= C + // [a] <= [declared param type] + // [declared return type] <= [x.m(a)] + + MethodRef method = expr.method(); + + Type returnType = method.returnType(); + + start(expr, returnType); + } + + // The return type of the method flows into the CallStaticExpr. + public void visitCallStaticExpr(CallStaticExpr expr) { + // m(a) + // [a] <= [declared param type] + // [declared return type] <= [m(a)] + + MethodRef method = expr.method(); + + Type returnType = method.returnType(); + + start(expr, returnType); + } + + // The type to which an expression is cast flows into the CastExpr. + public void visitCastExpr(CastExpr expr) { + // (C) a + // [(C) a] <= C + + start(expr, expr.castType()); + } + + // The type of the constant flows into the type of the ConstantExpr. + public void visitConstantExpr(ConstantExpr expr) { + // "a" + // String <= ["a"] + + Object value = expr.value(); + + if (value == null) { + start(expr, Type.NULL); + return; + } + + if (value instanceof String) { + start(expr, Type.STRING); + return; + } + + if (value instanceof Boolean) { + start(expr, Type.BOOLEAN); + return; + } + + if (value instanceof Integer) { + start(expr, Type.INT); + return; + } + + if (value instanceof Long) { + start(expr, Type.LONG); + return; + } + + if (value instanceof Float) { + start(expr, Type.FLOAT); + return; + } + + if (value instanceof Double) { + start(expr, Type.DOUBLE); + return; + } + + int v; + + if (value instanceof Byte) { + v = ((Byte) value).byteValue(); + + } else if (value instanceof Short) { + v = ((Short) value).shortValue(); + + } else if (value instanceof Character) { + v = ((Character) value).charValue(); + + } else { + throw new RuntimeException(); + } + + if (v >= 0) { + if (v <= 1) { + start(expr, Type.BOOLEAN); // It'll fit in a BOOLEAN + + } else if (v <= Byte.MAX_VALUE) { + start(expr, ClassHierarchy.POS_BYTE); + + } else if (v <= Short.MAX_VALUE) { + start(expr, ClassHierarchy.POS_SHORT); + + } else if (v <= Character.MAX_VALUE) { + start(expr, Type.CHAR); + + } else { + start(expr, Type.INT); + } + + } else { + // The constant's value is negative + if (Byte.MIN_VALUE <= v) { + start(expr, Type.BYTE); // It'll fit in a BYTE + + } else if (Short.MIN_VALUE <= v) { + start(expr, Type.SHORT); + + } else { + start(expr, Type.INT); + } + } + } + + // If the FieldExpr is a definition, the type of the field flows + // into the type of the FieldExpr. + public void visitFieldExpr(FieldExpr expr) { + // a.f, f in class C + // [a] <= C + // [declared field type] <= [a.f] + + MemberRef field = expr.field(); + + // if (! expr.isDef()) { + start(expr, field.type()); + // } + } + + // If the expression representing the type of the array being + // created is defined, then and array of that type flows into the + // type of the NewArrayExpr. + public void visitNewArrayExpr(NewArrayExpr expr) { + // new t[i] + // array-1-of-t <= [new t[i]] + + if (!expr.elementType().equals(TypeInference.UNDEF)) { + start(expr, new ArrayType(expr.elementType(), 1)); + } + } + + // The type of the object being created flows into the NewExpr. + public void visitNewExpr(NewExpr expr) { + // new C + // C <= [new C] + + start(expr, expr.objectType()); + } + + // If the type of the expression specifying the type of the array + // being created is defined, then that type flows into the type of + // the NewMultiArrayExpr. + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + // new t[i][j][k] + // array-dim-of-t <= [new t[i][j][k]] + + if (!expr.elementType().equals(TypeInference.UNDEF)) { + start(expr, new ArrayType(expr.elementType(), + expr.dimensions().length)); + } + } + + // If the field reference is a definition, then the type of the + // field flows into the type of the StaticFieldExpr. + public void visitStaticFieldExpr(StaticFieldExpr expr) { + // C.f + // [declared field type] <= [C.f] + + MemberRef field = expr.field(); + + if (!expr.isDef()) { + start(expr, field.type()); + } + } + + // Initializes the constraint propagation by assigning a type to a + // given expression. + private void start(Expr expr, Type type) { + if (TypeInference.DEBUG) { + System.out.println("start " + expr + " <- " + type); + } + + if (type.equals(TypeInference.UNDEF)) { + return; + } + + if (!expr.type().equals(TypeInference.UNDEF)) { + if (TypeInference.DEBUG) { + System.out.print("union of " + expr.type() + " and " + type); + } + + if (!Assert.isIntegral(type) + && !type.equals(ClassHierarchy.POS_BYTE) + && !type.equals(ClassHierarchy.POS_SHORT)) { + Assert.isTrue(Assert.simple(type).equals( + Assert.simple(expr.type()))); + + if (type instanceof ReferenceType) { + // The expr and type may have different types. So, deal + // with the common supertype of both type and + // expr.type(). + // That is, the type to which both belong. + + type = hier.unionType(type, expr.type()); + } + + } else { + // We're dealing with one of the integral types. Take the + // union of the two types and work with that. + BitSet v1 = ClassHierarchy.typeToSet(type); + BitSet v2 = ClassHierarchy.typeToSet(expr.type()); + v1.or(v2); + type = ClassHierarchy.setToType(v1); + } + + if (TypeInference.DEBUG) { + System.out.println(" is " + type); + } + } + + // Set the type of all nodes equivalent to expr to the type we're + // working with. + Iterator iter = ssaGraph.equivalent(expr).iterator(); + + while (iter.hasNext()) { + Node node = (Node) iter.next(); + + if (node instanceof Expr) { + Expr e = (Expr) node; + + if (e.setType(type)) { + changed = true; + } + } + } + } + + // Propagates the type of the source expression to all expressions + // equivalent to the other expr. + private void prop(Expr expr, Expr source) { + if (TypeInference.DEBUG) { + System.out.println("prop " + expr + " <- " + source); + } + start(expr, source.type()); + } + +} diff --git a/src/edu/purdue/cs/bloat/tbaa/package.html b/src/edu/purdue/cs/bloat/tbaa/package.html new file mode 100644 index 0000000..dd444d4 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tbaa/package.html @@ -0,0 +1,12 @@ + + + +

Performs type-base alias analysis (TBAA) on a control flow graph. +Type-based alias analysis considers the types of variables when +deciding whether or not two variable can refer to the same object. +Before TBAA can occur, the types of variables are inferred. Type +inference requires information about the class hierarchy of classes +being BLOATed.

+ + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/tests/BloatTester1.java b/src/edu/purdue/cs/bloat/tests/BloatTester1.java new file mode 100644 index 0000000..8976766 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tests/BloatTester1.java @@ -0,0 +1,19 @@ +package edu.purdue.cs.bloat.tests; + +public class BloatTester1 { + + int testDepth(int a) throws Exception { + while (String.class != null) + if (this.getClass().getMethods()[0].hashCode() == a) + do { + try { + this.getClass().getMethod("testDepth", + new Class[] { Integer.TYPE }).invoke(this, + new Object[] { new Integer(2) }); + } catch (ArrayIndexOutOfBoundsException e) { + throw new ClassNotFoundException("foo", e); + } + } while (0 == 5); + return 0; + } +} diff --git a/src/edu/purdue/cs/bloat/tests/BloatTester2.java b/src/edu/purdue/cs/bloat/tests/BloatTester2.java new file mode 100644 index 0000000..5748b0e --- /dev/null +++ b/src/edu/purdue/cs/bloat/tests/BloatTester2.java @@ -0,0 +1,23 @@ +package edu.purdue.cs.bloat.tests; + +public class BloatTester2 { + + public static BloatTester2 frob() throws Exception { + try { + System.in.close(); + System.out.println((new Object() { + public String snap() { + try { + return "" + "snap"; + } catch (NullPointerException e) { + throw new IllegalArgumentException(e.toString()); + } + } + }).snap()); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } + + return null; + } +} diff --git a/src/edu/purdue/cs/bloat/tests/BloatTester3.java b/src/edu/purdue/cs/bloat/tests/BloatTester3.java new file mode 100644 index 0000000..6aa786b --- /dev/null +++ b/src/edu/purdue/cs/bloat/tests/BloatTester3.java @@ -0,0 +1,25 @@ +package edu.purdue.cs.bloat.tests; + +public class BloatTester3 { + void frob() { + try { + int x = 5; + int y = 50 * x; + + if (x > 9) + throw new ClassNotFoundException(); + + if (y < 99) { + try { + String in = "elvis"; + in.toLowerCase(); + + } catch (Exception e) { + ; + } + } + } catch (ClassNotFoundException e) { + ; + } + } +} diff --git a/src/edu/purdue/cs/bloat/tests/Makefile b/src/edu/purdue/cs/bloat/tests/Makefile new file mode 100644 index 0000000..d8cceb3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tests/Makefile @@ -0,0 +1,28 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + BloatTester1.class \ + BloatTester2.class \ + BloatTester3.class + +include ../class.mk + diff --git a/src/edu/purdue/cs/bloat/trans/DeadCodeElimination.java b/src/edu/purdue/cs/bloat/trans/DeadCodeElimination.java new file mode 100644 index 0000000..e0803d3 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/DeadCodeElimination.java @@ -0,0 +1,712 @@ +/* + * Class: DeadCodeElimintation + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

+ * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

+ * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

+ * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.tree.AddressStoreStmt; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallMethodExpr; +import edu.purdue.cs.bloat.tree.CallStaticExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.DefExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.JumpStmt; +import edu.purdue.cs.bloat.tree.LabelStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MonitorStmt; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.RCExpr; +import edu.purdue.cs.bloat.tree.RetStmt; +import edu.purdue.cs.bloat.tree.ReturnExprStmt; +import edu.purdue.cs.bloat.tree.ReturnStmt; +import edu.purdue.cs.bloat.tree.SCStmt; +import edu.purdue.cs.bloat.tree.SRStmt; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.ThrowStmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.UCExpr; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.tree.ZeroCheckExpr; +import edu.purdue.cs.bloat.util.Assert; + +import java.util.*; + +/** + * DeadCodeElimination performs SSA-based dead code elimination as described in + * [Cytron, et. al. 91]. The idea behind dead code elimination is that there are + * some instructions that do not contribute anything useful to the result of the + * program. Most dead code is introduced by other optimizations. + * + * A program statement is live if one or more of the following holds: + * + *

    + * + *
  1. The statement effects program output. In Java this is a lot more than + * just I/O. We must be conservative and assume that exceptions and monitor + * expression are always live. + * + *
  2. The statement is an assignment statement whose target is used in a live + * statement. + * + *
  3. The statement is a conditional branch and there are live statements + * whose execution depend on the conditional branch. + * + *
      + * + * Basically, the algorithm proceeds by marking a number of statements as being + * pre-live and then uses a worklist to determine which statements must also be + * live by the above three conditions. + */ +public class DeadCodeElimination implements Optimization { + public static boolean DEBUG = false; + + private static final int DEAD = 0; + + private static final int LIVE = 1; + + // Keep a work list of expressions that need to be made live. + LinkedList worklist; + + public void transform(MethodState state) { + transform(state.controlFlowGraph()); + } + + /** + * Performs dead code elimination. + */ + private void transform(FlowGraph cfg) { + // Mark all nodes in the tree as DEAD. + cfg.visit(new TreeVisitor() { + public void visitNode(Node node) { + node.visitChildren(this); + node.setKey(DEAD); + } + }); + + worklist = new LinkedList(); + + // Visit the nodes in the tree and mark nodes that we know must be + // LIVE. + cfg.visit(new TreeVisitor() { + public void visitMonitorStmt(MonitorStmt stmt) { + // NullPointerException, IllegalMonitorStateException + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitInitStmt(InitStmt stmt) { + // Needed to correctly initialize the formal parameters when + // coloring + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitJsrStmt(JsrStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitRetStmt(RetStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSRStmt(SRStmt stmt) { + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSCStmt(SCStmt stmt) { + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitNewExpr(NewExpr expr) { + // Memory allocation + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitStackExpr(StackExpr expr) { + if (expr.stmt() instanceof PhiStmt) { + return; + } + + // Stack change + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + // NullPointerException or DivideByZeroException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitRCExpr(RCExpr expr) { + // Residency check + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitUCExpr(UCExpr expr) { + // Update check + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCastExpr(CastExpr expr) { + // ClassCastException + if (expr.castType() instanceof ReferenceType) { + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitArithExpr(ArithExpr expr) { + // DivideByZeroException + if (expr.operation() == ArithExpr.DIV + || expr.operation() == ArithExpr.REM) { + + if (Assert.isIntegral(expr.type())) { + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + return; + } + } + + expr.visitChildren(this); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + // NullPointerException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + // NullPointerException, ArrayIndexOutOfBoundsException, + // ArrayStoreException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitFieldExpr(FieldExpr expr) { + // NullPointerException + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + // Call + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + // Call + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCatchExpr(CatchExpr expr) { + // Stack change + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + // Stack change + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitThrowStmt(ThrowStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSwitchStmt(SwitchStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitIfStmt(IfStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitGotoStmt(GotoStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitReturnStmt(ReturnStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + // Branch + if (DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitStoreExpr(StoreExpr expr) { + // Can change a variable visible outside the method. + if (!(expr.target() instanceof LocalExpr)) { + if (DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + + } else { + expr.visitChildren(this); + } + } + }); + + // Go through the nodes in the worklist and make the nodes that + // defined the VarExprs live. + while (!worklist.isEmpty()) { + VarExpr expr = (VarExpr) worklist.removeFirst(); + + DefExpr def = expr.def(); + + if (def != null) { + if (DEBUG) { + System.out.println("making live def of " + expr); + System.out.println(" def = " + def); + } + + makeLive(def.parent()); + } + } + + // Remove dead stores. + cfg.visit(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + Expr lhs = expr.target(); + Expr rhs = expr.expr(); + + if (lhs.key() == DEAD && rhs.key() == LIVE) { + rhs.setParent(null); + expr.replaceWith(rhs, false); + + lhs.cleanup(); + expr.cleanupOnly(); + + lhs.setKey(DEAD); + expr.setKey(DEAD); + + rhs.visit(this); + + } else { + expr.visitChildren(this); + } + } + }); + + // Pull out live expressions from their dead parents. Gee, Nate, + // what a lovely sentiment. I'll think I'll send that one to + // Hallmark. + cfg.visit(new TreeVisitor() { + public void visitStmt(Stmt stmt) { + if (stmt.key() == DEAD) { + stmt.visitChildren(this); + } + } + + public void visitExpr(Expr expr) { + if (expr.key() == DEAD) { + expr.visitChildren(this); + return; + } + + Node parent = expr.parent(); + + if (parent.key() == LIVE) { + // expr will removed later + return; + } + + if (parent instanceof ExprStmt) { + // The expr and its parent are both dead, but expr + // resides + // in an ExprStmt. We want the parent after all. + parent.setKey(LIVE); + return; + } + + // We are going to remove the expr's parent, but keep the + // expr. Add eval(expr) [ExprStmt] before the stmt containing + // the expr. This is safe, since any exprs to the left in the + // statement's tree which are live have already been + // extracted. + + Stmt oldStmt = expr.stmt(); + + Tree tree = parent.block().tree(); + + // Replace the expr with an unused stack expr. + StackExpr t = tree.newStack(expr.type()); + expr.replaceWith(t, false); + t.setValueNumber(expr.valueNumber()); + + ExprStmt stmt = new ExprStmt(expr); + stmt.setValueNumber(expr.valueNumber()); + stmt.setKey(LIVE); + + tree.addStmtBefore(stmt, oldStmt); + + // The old statement is dead and will be removed later. + Assert.isTrue(oldStmt.key() == DEAD, oldStmt + + " should be dead"); + } + }); + + // Finally, remove the dead statements from the Tree. + cfg.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + Iterator e = tree.stmts().iterator(); + + while (e.hasNext()) { + Stmt stmt = (Stmt) e.next(); + + if (stmt.key() == DEAD) { + if (stmt instanceof LabelStmt) { + continue; + } + + if (stmt instanceof JumpStmt) { + continue; + } + + if (DEBUG) { + System.out.println("Removing DEAD " + stmt); + } + + e.remove(); + } + } + } + }); + + worklist = null; + } + + // /** + // * Make all of a statement's children LIVE. I don't think its used. + // * + // * @param stmt + // * A statement whose children to make live. + // */ + // void reviveStmt(Stmt stmt) { + // stmt.visit(new TreeVisitor() { + // public void visitExpr(Expr expr) { + // expr.setKey(LIVE); + // expr.visitChildren(this); + // } + // }); + // } + + /** + * Make a node and all of its children (recursively) LIVE. + * + * @param node + * A node to make LIVE. + */ + void makeLive(Node node) { + + if (node instanceof StoreExpr) { + // Make the StoreExpr, its target, and its RHS live. Add the + // target and the RHS to the worklist. + + StoreExpr expr = (StoreExpr) node; + + if (expr.key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(LIVE); + } + + if (expr.target().key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + expr.target() + " in " + + expr); + } + + expr.target().setKey(LIVE); + + if (expr.target() instanceof VarExpr) { + worklist.add(expr.target()); + } + } + + if (expr.expr().key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + expr.expr() + " in " + + expr); + } + + expr.expr().setKey(LIVE); + + if (expr.expr() instanceof VarExpr) { + worklist.add(expr.expr()); + } + } + } + + if (node instanceof Expr) { + // If one expression inside an ExprStmt is live, then the entire + // ExprStmt is live. + Node parent = ((Expr) node).parent(); + + if (parent instanceof ExprStmt) { + node = parent; + } + } + + node.visit(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + // Don't make local variable targets live yet. If the + // variable is used in a live expression, the target will be + // made live later. + if (expr.target() instanceof LocalExpr) { + expr.expr().visit(this); + + } else { + visitExpr(expr); + } + } + + public void visitVarExpr(VarExpr expr) { + if (expr.key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(LIVE); + worklist.add(expr); + } + } + + public void visitExpr(Expr expr) { + if (expr.key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(LIVE); + } + + expr.visitChildren(this); + } + + public void visitStmt(Stmt stmt) { + if (stmt.key() == DEAD) { + if (DEBUG) { + System.out.println("making live " + stmt); + } + + stmt.setKey(LIVE); + } + + stmt.visitChildren(this); + } + }); + } + + public String traceMessage(String dateString) { + return " Dead Code Elimination: " + dateString; + } + + public String preDebugMessage() { + return "---Before Dead Code Elimination--"; + } + + public String postDebugMessage() { + return "---After Dead Code Elimination---"; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/ExprPropagation.java b/src/edu/purdue/cs/bloat/trans/ExprPropagation.java new file mode 100644 index 0000000..3928708 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/ExprPropagation.java @@ -0,0 +1,301 @@ +/* + * Class: ExprPropagation + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import java.util.Iterator; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.LeafExpr; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MemExpr; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.ReplaceVisitor; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; + +/** + * Performs copy and constant propagation on the blocks in a control flow graph. + */ +public class ExprPropagation implements Optimization { + public static boolean DEBUG = false; + + FlowGraph cfg; + + boolean changed; // Did the cfg change? + + public void transform(MethodState state) { + transform(state.controlFlowGraph()); + } + + /** + * Performs the propagation. + */ + private void transform(FlowGraph cfg) { + this.cfg = cfg; + changed = true; + + while (changed) { + changed = false; + propagate(); + } + } + + /** + * Propagate expressions through the control flow graph in hopes of reducing + * the number of local variables. + */ + private void propagate() { + cfg.visit(new TreeVisitor() { + Iterator iter; + + public void visitTree(Tree tree) { + iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + Stmt stmt = (Stmt) iter.next(); + stmt.visit(this); + } + } + + public void visitStoreExpr(StoreExpr expr) { + expr.visitChildren(this); + + if (!(expr.target() instanceof LocalExpr)) { + // If we're not assigning to a local variable, fergit it + return; + } + + LocalExpr lhs = (LocalExpr) expr.target(); + Expr rhs = expr.expr(); + + // L := (M := E) + // use L + // use M + // + // --> + // + // L := E + // use L + // use L + // + // Since we've already visited (M := E), M could not be + // eliminated. So, after propagating M to L, we won't be + // able to eliminate L either, so don't even try. + // + if (rhs instanceof StoreExpr) { + StoreExpr store = (StoreExpr) rhs; + + MemExpr rhsLHS = store.target(); + Expr rhsRHS = store.expr(); + + if (rhsLHS instanceof LocalExpr) { + // Replace uses of M with L. + + // We need to make a copy of the lhs since it is a + // def an consequently does not contain a pointer to + // a def. + LocalExpr copy = (LocalExpr) lhs.clone(); + copy.setDef(lhs); + + if (propExpr(expr.block(), (LocalExpr) rhsLHS, copy)) { + // If all uses of the rhsRHS local variable were + // replaced, replace all occurrences of the rhs + // with the + // local variable of rhsRHS. + changed = true; + + expr.visit(new ReplaceVisitor(rhs, rhsRHS)); + rhsLHS.cleanup(); + rhs.cleanupOnly(); + } + + // Be sure to cleanup the copy. + copy.cleanup(); + } + + } + // This next part is awful and comented out. Propagating + // local variables like this fails to take into account + // the live ranges. When we have L := M, it replaces L with + // M after M has been overwritten. Arg. + /* + * else if (rhs instanceof LeafExpr) { if + * (propExpr(expr.block(), lhs, rhs)) { // If all uses of the + * local variable in the lhs were // replaced with the LeafExpr + * in the rhs, then the store // (L := X) is useless. Replace it + * with (eval X) so it // can be removed later. changed = true; // + * Replace eval (L := X) with eval X // Dead code elimination + * will remove it. if (expr.parent() instanceof ExprStmt) + * iter.remove(); else expr.replaceWith((Expr) rhs.clone()); } } + */ + } + + public void visitPhiStmt(PhiStmt stmt) { + Expr lhs = stmt.target(); + + if (!(lhs instanceof LocalExpr)) { + // If we're not assigning into a local variable, fergit + // it + return; + } + + // Look at all of the operands of the PhiStmt. If all of the + // operands are either the same local variable or the same + // constant, then propagate an operand (doesn't matter which + // one because they're all the same value) to all uses of the + // target of the PhiStmt. + Iterator e = stmt.operands().iterator(); + + if (!e.hasNext()) { + return; + } + + Expr rhs = (Expr) e.next(); + + if (!(rhs instanceof LeafExpr)) { + return; + } + + while (e.hasNext()) { + Expr operand = (Expr) e.next(); + + if (rhs instanceof LocalExpr) { + if (operand instanceof LocalExpr) { + if (rhs.def() != operand.def()) { + return; + } + } else { + return; + } + + } else if (rhs instanceof ConstantExpr) { + if (!rhs.equalsExpr(operand)) { + return; + } + + } else { + return; + } + } + + if (propExpr(stmt.block(), (LocalExpr) lhs, rhs)) { + // If all uses of the PhiStmt's target were replaced, + // remove + // it from the expression tree. + changed = true; + iter.remove(); + } + } + }); + } + + /** + * Propagates the expression in rhs to all uses of the lhs. Returns true, if + * all of the uses of the lhs were replaced. + */ + boolean propExpr(Block block, LocalExpr lhs, Expr rhs) { + if (DEBUG) { + System.out.println("prop " + rhs + " to uses of " + lhs); + System.out.println(" uses of lhs = " + lhs.uses()); + } + + if (rhs instanceof LocalExpr) { + // We can't prop a local to a PhiStmt operand, so don't bother + // doing the propagation at all. + Iterator e = lhs.uses().iterator(); + + while (e.hasNext()) { + LocalExpr use = (LocalExpr) e.next(); + + if (use.parent() instanceof PhiStmt) { + return false; + } + } + + // Replaces all uses of the lhs with the rhs. Both are local + // variables. + e = lhs.uses().iterator(); + + while (e.hasNext()) { + LocalExpr use = (LocalExpr) e.next(); + use.replaceWith((Expr) rhs.clone()); + } + + return true; + + } else { + boolean replacedAll = true; + + Iterator e = lhs.uses().iterator(); + + while (e.hasNext()) { + LocalExpr use = (LocalExpr) e.next(); + + if (use.parent() instanceof PhiCatchStmt) { + replacedAll = false; + } else { + use.replaceWith((Expr) rhs.clone()); + } + } + + return replacedAll; + } + } + + public String traceMessage(String dateString) { + return " Copy propagation: " + dateString; + } + + public String preDebugMessage() { + return "-----Before Copy Propagation-----"; + } + + public String postDebugMessage() { + return "------After Copy Propagation-----"; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/Makefile b/src/edu/purdue/cs/bloat/trans/Makefile new file mode 100644 index 0000000..9997c88 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/Makefile @@ -0,0 +1,36 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + CompactArrayInitializer.class\ + DeadCodeElimination.class\ + ExprPropagation.class\ + NodeComparator.class\ + Peephole.class\ + SSAPRE.class\ + SideEffectChecker.class\ + StackPRE.class\ + ValueFolder.class\ + ValueFolding.class\ + ValueNumbering.class + #PersistentCheckElimination.class\ + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/trans/NodeComparator.java b/src/edu/purdue/cs/bloat/trans/NodeComparator.java new file mode 100644 index 0000000..9096fcc --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/NodeComparator.java @@ -0,0 +1,594 @@ +/* + * Class: NodeComparator + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import edu.purdue.cs.bloat.tree.AddressStoreStmt; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallMethodExpr; +import edu.purdue.cs.bloat.tree.CallStaticExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfCmpStmt; +import edu.purdue.cs.bloat.tree.IfZeroStmt; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.InstanceOfExpr; +import edu.purdue.cs.bloat.tree.JsrStmt; +import edu.purdue.cs.bloat.tree.LabelStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MonitorStmt; +import edu.purdue.cs.bloat.tree.NegExpr; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.RCExpr; +import edu.purdue.cs.bloat.tree.RetStmt; +import edu.purdue.cs.bloat.tree.ReturnAddressExpr; +import edu.purdue.cs.bloat.tree.ReturnExprStmt; +import edu.purdue.cs.bloat.tree.ReturnStmt; +import edu.purdue.cs.bloat.tree.SCStmt; +import edu.purdue.cs.bloat.tree.SRStmt; +import edu.purdue.cs.bloat.tree.ShiftExpr; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.ThrowStmt; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.UCExpr; +import edu.purdue.cs.bloat.tree.ZeroCheckExpr; + +/** + * NodeComparator is a class used to differentiate nodes in an expression tree. + */ +public class NodeComparator { + public static boolean DEBUG = false; + + /** + * Determines whether or not two Nodes are equal. + */ + public static boolean equals(final Node v, final Node w) { + class Bool { + boolean value = false; + } + ; + + final Bool eq = new Bool(); + + v.visit(new TreeVisitor() { + public void visitExprStmt(ExprStmt stmt) { + if (w instanceof ExprStmt) { + eq.value = true; + } + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + if (w instanceof IfCmpStmt) { + IfCmpStmt s = (IfCmpStmt) w; + eq.value = stmt.trueTarget() == s.trueTarget() + && stmt.falseTarget() == s.falseTarget() + && stmt.comparison() == s.comparison(); + } + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + if (w instanceof IfZeroStmt) { + IfZeroStmt s = (IfZeroStmt) w; + eq.value = stmt.trueTarget() == s.trueTarget() + && stmt.falseTarget() == s.falseTarget() + && stmt.comparison() == s.comparison(); + } + } + + public void visitSCStmt(SCStmt stmt) { + if (w instanceof SCStmt) { + SCStmt s = (SCStmt) w; + eq.value = stmt.array() == s.array() + && stmt.index() == s.index(); + } + } + + public void visitSRStmt(SRStmt stmt) { + if (w instanceof SRStmt) { + SRStmt s = (SRStmt) w; + eq.value = stmt.array() == s.array() + && stmt.start() == s.start() + && stmt.end() == s.end(); + } + } + + public void visitInitStmt(InitStmt stmt) { + if (w instanceof InitStmt) { + eq.value = true; + } + } + + public void visitGotoStmt(GotoStmt stmt) { + if (w instanceof GotoStmt) { + GotoStmt s = (GotoStmt) w; + eq.value = stmt.target() == s.target(); + } + } + + public void visitLabelStmt(LabelStmt stmt) { + if (w instanceof LabelStmt) { + LabelStmt s = (LabelStmt) w; + eq.value = stmt.label().equals(s.label()); + } + } + + public void visitMonitorStmt(MonitorStmt stmt) { + if (w instanceof MonitorStmt) { + MonitorStmt s = (MonitorStmt) w; + eq.value = stmt.kind() == s.kind(); + } + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + if (w instanceof PhiJoinStmt) { + eq.value = true; + } + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + if (w instanceof PhiCatchStmt) { + eq.value = true; + } + } + + public void visitCatchExpr(CatchExpr expr) { + // Catches are not equivalent. + eq.value = false; + } + + public void visitStackManipStmt(StackManipStmt stmt) { + if (w instanceof StackManipStmt) { + StackManipStmt s = (StackManipStmt) w; + eq.value = stmt.kind() == s.kind(); + } + } + + public void visitRetStmt(RetStmt stmt) { + if (w instanceof RetStmt) { + RetStmt s = (RetStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + if (w instanceof ReturnExprStmt) { + eq.value = true; + } + } + + public void visitReturnStmt(ReturnStmt stmt) { + if (w instanceof ReturnStmt) { + eq.value = true; + } + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + if (w instanceof AddressStoreStmt) { + AddressStoreStmt s = (AddressStoreStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitStoreExpr(StoreExpr expr) { + if (w instanceof StoreExpr) { + eq.value = true; + } + } + + public void visitJsrStmt(JsrStmt stmt) { + if (w instanceof JsrStmt) { + JsrStmt s = (JsrStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitSwitchStmt(SwitchStmt stmt) { + if (w instanceof SwitchStmt) { + eq.value = stmt == w; + } + } + + public void visitThrowStmt(ThrowStmt stmt) { + if (w instanceof ThrowStmt) { + eq.value = true; + } + } + + public void visitArithExpr(ArithExpr expr) { + if (w instanceof ArithExpr) { + ArithExpr e = (ArithExpr) w; + eq.value = e.operation() == expr.operation(); + } + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + if (w instanceof ArrayLengthExpr) { + eq.value = true; + } + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + if (w instanceof ArrayRefExpr) { + eq.value = true; + } + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + // Calls are never equal. + eq.value = false; + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + // Calls are never equal. + eq.value = false; + } + + public void visitCastExpr(CastExpr expr) { + if (w instanceof CastExpr) { + CastExpr e = (CastExpr) w; + eq.value = e.castType().equals(expr.castType()); + } + } + + public void visitConstantExpr(ConstantExpr expr) { + if (w instanceof ConstantExpr) { + ConstantExpr e = (ConstantExpr) w; + if (e.value() == null) { + eq.value = expr.value() == null; + } else { + eq.value = e.value().equals(expr.value()); + } + } + } + + public void visitFieldExpr(FieldExpr expr) { + if (w instanceof FieldExpr) { + FieldExpr e = (FieldExpr) w; + eq.value = e.field().equals(expr.field()); + } + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + if (w instanceof InstanceOfExpr) { + InstanceOfExpr e = (InstanceOfExpr) w; + eq.value = e.checkType().equals(expr.checkType()); + } + } + + public void visitLocalExpr(LocalExpr expr) { + if (w instanceof LocalExpr) { + LocalExpr e = (LocalExpr) w; + eq.value = e.def() == expr.def(); + } + } + + public void visitNegExpr(NegExpr expr) { + if (w instanceof NegExpr) { + eq.value = true; + } + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + eq.value = false; + } + + public void visitNewExpr(NewExpr expr) { + eq.value = false; + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + eq.value = false; + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + if (w instanceof ZeroCheckExpr) { + eq.value = true; + } + } + + public void visitRCExpr(RCExpr expr) { + if (w instanceof RCExpr) { + eq.value = true; + } + } + + public void visitUCExpr(UCExpr expr) { + if (w instanceof UCExpr) { + UCExpr e = (UCExpr) w; + eq.value = e.kind() == expr.kind(); + } + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + if (w instanceof ReturnAddressExpr) { + eq.value = true; + } + } + + public void visitShiftExpr(ShiftExpr expr) { + if (w instanceof ShiftExpr) { + ShiftExpr e = (ShiftExpr) w; + eq.value = e.dir() == expr.dir(); + } + } + + public void visitStackExpr(StackExpr expr) { + if (w instanceof StackExpr) { + StackExpr e = (StackExpr) w; + eq.value = e.def() == expr.def(); + } + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + if (w instanceof StaticFieldExpr) { + StaticFieldExpr e = (StaticFieldExpr) w; + eq.value = e.field().equals(expr.field()); + } + } + + public void visitNode(Node node) { + throw new RuntimeException("No method for " + node); + } + }); + + return eq.value; + } + + /** + * Computes a hash code for a given Node based upon its type. The + * hash code of nodes that are composed of other nodes are based upon their + * composits. + */ + public static int hashCode(final Node node) { + class Int { + int value = 0; + } + ; + + final Int hash = new Int(); + + node.visit(new TreeVisitor() { + public void visitExprStmt(ExprStmt stmt) { + hash.value = 1; + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + hash.value = stmt.comparison() + stmt.trueTarget().hashCode() + + stmt.falseTarget().hashCode(); + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + hash.value = stmt.comparison() + stmt.trueTarget().hashCode() + + stmt.falseTarget().hashCode(); + } + + public void visitInitStmt(InitStmt stmt) { + hash.value = 2; + } + + public void visitGotoStmt(GotoStmt stmt) { + hash.value = stmt.target().hashCode(); + } + + public void visitLabelStmt(LabelStmt stmt) { + hash.value = stmt.label().hashCode(); + } + + public void visitMonitorStmt(MonitorStmt stmt) { + hash.value = stmt.kind(); + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + hash.value = 3; + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + hash.value = 4; + } + + public void visitCatchExpr(CatchExpr expr) { + // Catches are not equivalent. + hash.value = expr.hashCode(); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + hash.value = stmt.kind(); + } + + public void visitRetStmt(RetStmt stmt) { + hash.value = 5; + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + hash.value = 6; + } + + public void visitReturnStmt(ReturnStmt stmt) { + hash.value = 7; + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + hash.value = 8; + } + + public void visitStoreExpr(StoreExpr expr) { + hash.value = 9; + } + + public void visitJsrStmt(JsrStmt stmt) { + hash.value = 10; + } + + public void visitSwitchStmt(SwitchStmt stmt) { + hash.value = 11; + } + + public void visitThrowStmt(ThrowStmt stmt) { + hash.value = 12; + } + + public void visitArithExpr(ArithExpr expr) { + hash.value = expr.operation(); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + hash.value = 13; + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + hash.value = 14; + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + // Calls are never equal. + hash.value = expr.hashCode(); + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + // Calls are never equal. + hash.value = expr.hashCode(); + } + + public void visitCastExpr(CastExpr expr) { + hash.value = expr.castType().hashCode(); + } + + public void visitConstantExpr(ConstantExpr expr) { + if (expr.value() == null) { + hash.value = 0; + } else { + hash.value = expr.value().hashCode(); + } + } + + public void visitFieldExpr(FieldExpr expr) { + hash.value = expr.field().hashCode(); + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + hash.value = expr.checkType().hashCode(); + } + + public void visitLocalExpr(LocalExpr expr) { + if (expr.def() != null) { + hash.value = expr.def().hashCode(); + } else { + hash.value = 0; + } + } + + public void visitNegExpr(NegExpr expr) { + hash.value = 16; + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitNewExpr(NewExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + hash.value = 15; + } + + public void visitRCExpr(RCExpr expr) { + hash.value = 17; + } + + public void visitUCExpr(UCExpr expr) { + hash.value = 18 + expr.kind(); + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + hash.value = 21; + } + + public void visitSCStmt(SCStmt stmt) { + hash.value = 23; + } + + public void visitSRStmt(SRStmt stmt) { + hash.value = 22; + } + + public void visitShiftExpr(ShiftExpr expr) { + hash.value = expr.dir(); + } + + public void visitStackExpr(StackExpr expr) { + if (expr.def() != null) { + hash.value = expr.def().hashCode(); + } else { + hash.value = 0; + } + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + hash.value = expr.field().hashCode(); + } + + public void visitNode(Node node) { + throw new RuntimeException("No method for " + node); + } + }); + + return hash.value; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/Peephole.java b/src/edu/purdue/cs/bloat/trans/Peephole.java new file mode 100644 index 0000000..c1be0a6 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/Peephole.java @@ -0,0 +1,978 @@ +/* + * Class: Peephole + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.util.Assert; + +import java.util.*; + +/** + * Performs some peephole optimizations such as loads and stores and removes + * unreachable instructions. + */ + +public class Peephole implements Optimization { + public static boolean DEBUG = false; + + public String traceMessage(String dateString) { + return null; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + public void transform(MethodState state) { + this.transform(state.methodGen(), true); + } + + /** + * Perform peephole optimizations on the bytecodes in a method. Peephole + * optimizations look at two consecutive instructions and try to perform + * some simple optimization. For instance, a push followed by a pop is + * uninteresting, so both of those instructions can be removed. + * + *

      + * + * After the peephole optimizations are performed a final phase removes + * instructions that are not reachable. These instructions reside in basic + * blocks whose starting label is never jumped to. + */ + + // Peephole can work on any bytecode sequence it does not have to be a + // result of calls + // to CodeGenerator. However, if Peephole will need to have labels denoting + // the start + // of basic blocks, if the code is not the result of output from + // CodeGenerator then labels + // will not be intact set "labels" to false if this is the case and they can + // be placed using + // FlowGraph.buildBLOATInstructionList(MethodGen) --Arrin + public void transform(MethodGen method, boolean labels) { + if (DEBUG) { + System.out.println("Peephole optimizing " + method); + } + + // Map between labels and the instruction that they label + Map targets = new LinkedHashMap(); + + LinkedList jumps = new LinkedList(); // Jump instructions + + Instruction next = null; + + InstructionList code = method.getInstructionList(); + // Do we need to build the labels are they already intact? + if (!labels) + FlowGraph.buildBLOATInstructionList(method); + + InstructionHandle ih = null; + // Go backwards so we can eliminate redundant loads and stores + // in one pass. During the pass collect the locations of labels. + boolean canFilter = true; + CODE: for (ih = code.getEnd(); ih != code.getStart(); ih = ih.getPrev()) { + + if (FlowGraph.label(ih)) { + targets.put(ih, ih.getInstruction()); + } + + // All items are Instructions now + Instruction inst = ih.getInstruction(); + + Filter peep = null; + + // Have we seen a label that starts a block? (i.e. is a target) + boolean seenLabel = false; + + if (inst instanceof GotoInstruction) { + // Look at the instructions following the goto. If an + // instruction follows and no label (that starts a block) + // has been seen, the instruction is dead and can be + // removed. If the target of the goto follows the goto + // instruction, the goto is useless and is removed. + + InstructionHandle target = ((GotoInstruction) inst).getTarget(); + + for (InstructionHandle j = ih.getNext(); j != null; j = j + .getNext()) { + + // Replace + // goto L + // L: inst + // with + // L: inst + // + if (FlowGraph.label(j)) { + // if (((Label) t).startsBlock()) { + // All labels start a block now + seenLabel = true; + + if (target.equals(j)) { + InstructionHandle toDelete = ih; + ih = ih.getNext(); + try { + code.delete(toDelete); + next = null; + } catch (TargetLostException tle) { + InstructionHandle[] losttargets = tle + .getTargets(); + for (int i = 0; i < losttargets.length; i++) { + InstructionTargeter[] targeters = losttargets[i] + .getTargeters(); + + for (int k = 0; k < targeters.length; k++) + targeters[k].updateTarget( + losttargets[i], j); + } + // Redirect Lost targeters + } + continue CODE; + } + if (Peephole.DEBUG) + System.out.println("Not a target instruction : " + + j.getInstruction()); + // Now we want to look at the instruction + // continue; + } + + // Replace + // goto L + // this is unreachable + // M: inst (M is a different label from L!) + // with + // goto L + // M: inst + // + // if (!FlowGraph.label(j)) { + if (seenLabel) { + break; + } + InstructionHandle j2 = j; + j = j.getPrev(); + try { + code.delete(j2); // Delete an unreachable instruction + } catch (TargetLostException tle) { + System.out.println("Got a lost Target!!!!!!!"); + // fail quitely + } + + // } + } + } + + if (inst instanceof GotoInstruction || inst instanceof Select + || inst instanceof IfInstruction) { + jumps.add(ih); + } + + // Performs some peephole optimizations using the filter + // method that returns an instance of the Filter class. The + // filter method looks at two consecutive instructions and + // determines whether or not something about them can be + // changed. For instance, if a push is followed by a pop, + // both instructions are useless and can be eliminated. The + // contents of the Filter object represents the effects of the + // peephole optimization. + if (next != null && canFilter) { + peep = filter(inst, next, method); + } + + if (peep != null) { + if (Peephole.DEBUG) { + if (peep.replace.length == 0) { + System.out.println("eliminate " + ih.getInstruction() + + "-" + ih.getNext().getInstruction()); + + } else { + System.out.println("replace " + ih.getInstruction() + + "-" + ih.getNext().getInstruction()); + System.out.println(" with"); + for (int j = 0; j < peep.replace.length; j++) { + System.out.println(" " + peep.replace[j]); + } + } + } + InstructionHandle oldIh = ih; + ih = ih.getPrev(); + InstructionHandle redirectTo = ih.getNext().getNext(); + // if no Instruction is added redirect to after the deleted + // instructions + if (ih == null) {// we are at the start of the + // InstructionList + // Add instructions resulting from peephole optimizations + for (int j = peep.replace.length - 1; j >= 0; j--) { + if (peep.replace[j] instanceof BranchInstruction) + redirectTo = code + .insert((BranchInstruction) peep.replace[j]); + else + // UGH BCEL treats Branch instructions differently + redirectTo = code.insert(peep.replace[j]); + // break; + } + } else { + for (int j = peep.replace.length - 1; j >= 0; j--) { + if (peep.replace[j] instanceof BranchInstruction) + redirectTo = code.append(ih, + (BranchInstruction) peep.replace[j]); + else + // UGH BCEL treats Branch instructions differently + redirectTo = code.append(ih, peep.replace[j]); + } + } + try { + // Remove old instructions + code.delete(oldIh.getNext()); + code.delete(oldIh); + } catch (TargetLostException tle) { + FlowGraph.setLabel(redirectTo); // reinstate as a label. + InstructionHandle[] losttargets = tle.getTargets(); + for (int i = 0; i < losttargets.length; i++) { + InstructionTargeter[] targeters = losttargets[i] + .getTargeters(); + + for (int j = 0; j < targeters.length; j++) + targeters[j].updateTarget(losttargets[i], + redirectTo); + } + // Redirect Lost targeters + } + + ih = ih.getNext();// This should be one of the added + // instructions if one was added. + if (ih != null && !FlowGraph.label(ih)) { + next = ih.getInstruction(); + + } else { + // No more instructions, or next thing is a label + next = null; + } + + } else { + if (ih != null && !FlowGraph.label(ih)) { + next = ih.getInstruction(); + } else { + next = null; + } + // Filter didn't find any peephole optimizations, skip to + // next pair of instructions. + } + + } + + if (DEBUG) { + System.out.println("Peephole optimizing removing GOTOs"); + } + + // Replace the target of jumps to gotos with the goto target. + // Replace gotos to unconditional jumps with the unconditional jump. + while (!jumps.isEmpty()) { + InstructionHandle iHandle = (InstructionHandle) jumps.removeFirst(); + InstructionHandle target; + + if ((iHandle.getInstruction() instanceof GotoInstruction) + || (iHandle.getInstruction() instanceof IfInstruction)) { + BranchInstruction inst = (BranchInstruction) iHandle + .getInstruction(); + target = inst.getTarget(); + if (target != null) { + Instruction targetInst = target.getInstruction(); + if (targetInst instanceof GotoInstruction + && !(((GotoInstruction) targetInst).getTarget() + .equals(inst.getTarget()))) { + // IF THIS WASN'T TRUE WOULDN'T IT MEAN WE HAVE A GOTO + // DIRECTED TO JUMP TO ITSELF? + if (Peephole.DEBUG) { + System.out.println("replace " + inst); + } + + inst.setTarget(((GotoInstruction) targetInst) + .getTarget()); + + if (Peephole.DEBUG) { + System.out.println(" with " + inst); + } + + jumps.add(iHandle); + + } else if ((inst instanceof GotoInstruction) + && (targetInst instanceof Select || + // targetInst instanceof ReturnInstruction || + targetInst instanceof ATHROW)) { + + if (Peephole.DEBUG) { + System.out.println("replace " + inst); + } + + iHandle.setInstruction((BranchInstruction) targetInst + .copy()); + // THIS MIGHT BE BAD copy() IS A SHALLOW COPY!! + + if (Peephole.DEBUG) { + System.out.println(" with " + targetInst); + } + } + } + } + } + // Remove unreachable code. + removeUnreachable(method, code); + + Iterator iterhandles = method.getInstructionList().iterator(); + while (iterhandles.hasNext()) { + ih = (InstructionHandle) iterhandles.next(); + ih.removeAttribute("Label"); + } + + method.removeNOPs(); + method.getInstructionList().setPositions(); + method.setMaxStack(); + method.setMaxLocals(); + method.update(); + + if (Peephole.DEBUG) { + System.out.println("END PEEPHOLE--------------------------------"); + } + } + + /** + * Iterate over the code in the method and determine which labels begin + * blocks that are reachable. The code in blocks that are not reachable is + * removed. + */ + // TODO: Currently, ALL ret targets are marked reachable from a + // single ret. Correct this by looking at the local variables. + private void removeUnreachable(MethodGen method, InstructionList code) { + // Maps Labels to their instruction position + Map labelPos = new LinkedHashMap(); + + // Collect all the ret targets. + Iterator iter = code.iterator(); + int i = 0; + + while (iter.hasNext()) { + InstructionHandle ce = (InstructionHandle) iter.next(); + + if (FlowGraph.label(ce)) { + labelPos.put(ce, new Integer(i)); + } + + i++; + } + + // Visit the blocks depth-first. + + // Stack of Labels that begin blocks that have been visited + Set visited = new LinkedHashSet(); + + // Stack of Labels that begin blocks that have not been visited + Stack stack = new Stack(); + + InstructionHandle label; // Current label + + if (code.getLength() > 0) { + // Start with the label of the first block + label = (InstructionHandle) code.getStart(); + visited.add(label); + stack.push(label); + } + + CodeExceptionGen[] excptns = method.getExceptionHandlers(); + + for (i = 0; i < excptns.length; i++) { + // All exception handlers are considered to be live + visited.add(excptns[i].getHandlerPC()); + stack.push(excptns[i].getHandlerPC()); + } + + while (!stack.isEmpty()) { + label = (InstructionHandle) stack.pop(); + + Integer labelIndex = (Integer) labelPos.get(label); + Assert.isTrue(labelIndex != null, "Index of label_" + + label.getPosition() + " not found"); + + InstructionHandle blockIter = label; + + while (blockIter != null && blockIter.getNext() != null) { + // Iterate over the code in the block. If we encounter + // instructions that change execution (i.e. go to another + // block), add the Label of the target of the jump to the + // stack if it is not already present. + + blockIter = blockIter.getNext(); + Instruction inst = blockIter.getInstruction(); + + if (!FlowGraph.label(blockIter)) { + if (inst instanceof ReturnInstruction + || inst instanceof ATHROW) { + // We've reached the end of the block, but we don't know + // which block will be executed next. + break; + + } else if (inst instanceof IfInstruction + || inst instanceof JsrInstruction) { + // We've reached the end of the block, add the Label of + // the next block to be executed to the list. It's a + // conditional jump, so don't break. The rest of the + // code in the block is not necessarily dead. + + label = ((BranchInstruction) inst).getTarget(); + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + // Fall through. + + } else if (inst instanceof GotoInstruction) { + // Add next block to work list. + + label = ((GotoInstruction) inst).getTarget(); + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + break; + + } else if (inst instanceof RET) { + // The ret targets were handled by the jsr. + break; + + } else if (inst instanceof Select) { + // A switch. Add all possible targets of the switch to + // the worklist. + + Select sw = (Select) inst; + + label = sw.getTarget(); + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + InstructionHandle[] targets = sw.getTargets(); + + for (int j = 0; j < targets.length; j++) { + label = targets[j]; + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + } + + break; + } + } else if (FlowGraph.label(blockIter)) { + label = blockIter; + visited.add(label); + } + } + } + if (DEBUG) { + System.out.println("Done finding unreachable instructions"); + } + + boolean reachable = false; + + iter = code.iterator(); + + // Remove unreachable instructions + while (iter.hasNext()) { + InstructionHandle ce = (InstructionHandle) iter.next(); + + if (FlowGraph.label(ce)) { + reachable = visited.contains(ce); + // Don't remove unreachable labels, only instructions. + + } + if (!reachable) { + if (Peephole.DEBUG) { + System.out.println("Removing unreachable " + ce); + } + iter.remove(); + } + } + } + + /** + * Filter represents a set of instructions that result from a peephole + * optimizations. For instance, when uninteresting instructions are removed, + * a Filter object with an empty "replace" array will be returned by the + * below filter method. + */ + static class Filter { + Instruction[] replace; + + boolean[] labels; + + Filter() { + this.replace = new Instruction[0]; + } + + Filter(Instruction replace) { + this.replace = new Instruction[] { replace }; + } + + Filter(Instruction replace1, Instruction replace2) { + this.replace = new Instruction[] { replace1, replace2 }; + } + } + + /** + * Filter a pair of instructions. That is, do a peephole optimization on two + * consecutive instructions. For instance, if a push is followed by a pop, + * both instructions can be eliminated. The Filter object that is + * returned specifies what instruction(s), if any, should replace the two + * instructions that are the parameters to this method. + * + * @param first + * The first instruction. + * @param second + * The second instruction. + * @return A list of instructions to replace the two instructions with, or + * null, if the instructions should be left as is. + */ + private Filter filter(Instruction first, Instruction second, + MethodGen method) { + // switch (second.opcodeClass()) { + + // swap means nothing if it's after a dup. + // (goodbye means nothing when it's all for show + // so stop pretending you've somewhere else to go.) + + // case opcx_swap: + if (second instanceof SWAP) { + // Elminate swap-swap + if (first instanceof SWAP) + return new Filter(); + // swap means nothing if it's after a dup. + // (goodbye means nothing when it's all for show + // so stop pretending you've somewhere else to go.) + if (first instanceof DUP) { + return new Filter(first); + } + } else if (second instanceof POP) {// Eliminate push-pop. + // Eliminate push-pop. + + // switch (first.opcodeClass()) { + if (first instanceof LDC) { + LDC f = (LDC) first; + // Make sure things being popped off is not wide (we're + // dealing with a pop not a pop2). + Assert.isTrue(!(f.getType(method.getConstantPool()) + .equals(Type.LONG)) + && !(f.getType(method.getConstantPool()) + .equals(Type.DOUBLE)), + "Cannot pop a 2-word operand"); + return new Filter(); + } else if ((first instanceof ILOAD) || (first instanceof FLOAD) + || (first instanceof ALOAD) || (first instanceof DUP)) { + // Eliminate the load and the pop. + return new Filter(); + } else if (first instanceof DUP_X1) { + // Replace dup_x1-pop with swap + // (As if this is really likely to happen ;) <-- Nate made a + // joke! + System.out.println("HERE0 inserting SWAP"); + return new Filter(new SWAP()); + } + } else if (second instanceof POP2) { + if (first instanceof LDC) { + LDC f = (LDC) first; + Assert.isTrue((f.getType(method.getConstantPool()) + .equals(Type.LONG)) + || (f.getType(method.getConstantPool()) + .equals(Type.DOUBLE)), + "Cannot pop2 a 1-word operand"); + return new Filter(); + } else if ((first instanceof LLOAD) || (first instanceof DLOAD) + || (first instanceof DUP2)) { + // Eliminate push and pop + return new Filter(); + } + } else if (second instanceof ISTORE) { + StoreInstruction secnd = (StoreInstruction) second; + // Eliminate load-store to same location. + if (first instanceof ILOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(); + } + } + } else if (second instanceof FSTORE) { + StoreInstruction secnd = (StoreInstruction) second; + if (first instanceof FLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(); + } + } + } else if (second instanceof ASTORE) { + StoreInstruction secnd = (StoreInstruction) second; + if (first instanceof ALOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(); + } + } + } else if (second instanceof LSTORE) { + StoreInstruction secnd = (StoreInstruction) second; + if (first instanceof LLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(); + } + } + } else if (second instanceof DSTORE) { + StoreInstruction secnd = (StoreInstruction) second; + if (first instanceof DLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(); + } + } + } else if (second instanceof ReturnInstruction) { + // THIS APPLIES FOR RETURN TOO RIGHT? + // Replace store-return with return. Remember that upon return + // all local variables revert to their pre-call values, so any + // stores are destroyed. + if (first instanceof StoreInstruction) { + return new Filter(second); + } + } else if (second instanceof IADD) { + // Replace ineg-iadd with isub + if (first instanceof INEG) { + return new Filter(new ISUB()); + } + } else if (second instanceof ISUB) { + // Replace ineg-isub with iadd + if (first instanceof INEG) { + return new Filter(new IADD()); + } + } else if (second instanceof LADD) { + // Replace lneg-ladd with lsub + if (first instanceof LNEG) { + return new Filter(new LSUB()); + } + } else if (second instanceof LSUB) { + // Replace lneg-lsub with ladd + if (first instanceof LNEG) { + return new Filter(new LADD()); + } + } else if (second instanceof IF_ICMPEQ) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmpeq with ifeq + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFEQ(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ICMPNE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmpne with ifne + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFNE(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ICMPLT) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmplt with iflt + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFLT(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ICMPGE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmpge with ifge + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFGE(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ICMPGT) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmpgt with ifgt + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFGT(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ICMPLE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-if_icmple with ifle + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new IFLE(secnd.getTarget())); + } + } + } + } else if (second instanceof IF_ACMPEQ) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc null-if_acmpeq with ifnull + if (first instanceof LDC) { + LDC frst = (LDC) first; + if (frst.getValue(method.getConstantPool()) == null) { + return new Filter(new IFNULL(secnd.getTarget())); + } + } + } else if (second instanceof IF_ACMPNE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc null-if_acmpne with ifnonnull + if (first instanceof LDC) { + LDC frst = (LDC) first; + if (frst.getValue(method.getConstantPool()) == null) { + return new Filter(new IFNONNULL(secnd.getTarget())); + } + } + } else if (second instanceof IFEQ) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc 0-ifeq with goto and eliminate ldc !0-ifeq + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } else if (second instanceof IFNE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc !0-ifne with goto and eliminate ldc 0-ifne + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() != 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } else if (second instanceof IFLT) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc <0-iflt with goto and eliminate ldc >=0-iflt + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() < 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } else if (second instanceof IFGE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc >=0-ifge with goto and eliminate ldc <0-ifge + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() >= 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } else if (second instanceof IFGT) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc >0-ifgt with goto and eliminate ldc <=0-ifgt + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() > 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } else if (second instanceof IFLE) { + BranchInstruction secnd = (BranchInstruction) second; + // Replace ldc <=0-ifle with goto and eliminate ldc >0-ifle + if (first instanceof LDC) { + LDC frst = (LDC) first; + Object op = frst.getValue(method.getConstantPool()); + if (op instanceof Integer) { + if (((Integer) op).intValue() <= 0) { + return new Filter(new GOTO(secnd.getTarget())); + } else { + return new Filter(); + } + } + } + } + + if (second instanceof StoreInstruction) { + StoreInstruction secnd = (StoreInstruction) second; + // Replace store-store to same location with pop-store. + if (first instanceof StoreInstruction) { + StoreInstruction frst = (StoreInstruction) first; + if ((frst instanceof LSTORE) || (frst instanceof DSTORE)) { + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new POP2(), first); + } + } else { + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new POP(), first); + } + } + } + } + // Replace store-load with dup-store. + // Replace load-load with load-dup. + if (second instanceof ILOAD) { + LoadInstruction secnd = (LoadInstruction) second; + if (first instanceof ISTORE) { + StoreInstruction frst = (StoreInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new DUP(), first); + } + } + if (first instanceof ILOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(first, new DUP()); + } + } + } else if (second instanceof FLOAD) { + LoadInstruction secnd = (LoadInstruction) second; + if (first instanceof FSTORE) { + StoreInstruction frst = (StoreInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new DUP(), first); + } + } + if (first instanceof FLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(first, new DUP()); + } + } + } else if (second instanceof ALOAD) { + LoadInstruction secnd = (LoadInstruction) second; + if (first instanceof ASTORE) { + StoreInstruction frst = (StoreInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new DUP(), first); + } + } + if (first instanceof ALOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(first, new DUP()); + } + } + } else if (second instanceof LLOAD) { + LoadInstruction secnd = (LoadInstruction) second; + if (first instanceof LSTORE) { + StoreInstruction frst = (StoreInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new DUP2(), first); + } + } + if (first instanceof LLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(first, new DUP2()); + } + } + } else if (second instanceof DLOAD) { + LoadInstruction secnd = (LoadInstruction) second; + if (first instanceof DSTORE) { + StoreInstruction frst = (StoreInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(new DUP2(), first); + } + } + if (first instanceof DLOAD) { + LoadInstruction frst = (LoadInstruction) first; + if (frst.getIndex() == secnd.getIndex()) { + return new Filter(first, new DUP2()); + } + } + } + return null; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/SSAPRE.java b/src/edu/purdue/cs/bloat/trans/SSAPRE.java new file mode 100644 index 0000000..999c81e --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/SSAPRE.java @@ -0,0 +1,3882 @@ +/* + * Class: SSAPRE + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.Field; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Handler; +import edu.purdue.cs.bloat.context.BloatContext; +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.ssa.SSA; +import edu.purdue.cs.bloat.ssa.SSAConstructionInfo; +import edu.purdue.cs.bloat.tbaa.TBAA; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.CheckExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.ExprStmt; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.InstanceOfExpr; +import edu.purdue.cs.bloat.tree.LabelStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MemRefExpr; +import edu.purdue.cs.bloat.tree.MonitorStmt; +import edu.purdue.cs.bloat.tree.NegExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.PrintVisitor; +import edu.purdue.cs.bloat.tree.ReplaceVisitor; +import edu.purdue.cs.bloat.tree.ShiftExpr; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.Stmt; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.VarExpr; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.ResizeableArrayList; + +import java.util.*; + +/** + * Perform partial redundancy elimination of a CFG in SSA form using the + * SSA-based algorithm described in: + * + *

      + *        Fred Chow, Sun Chan, Robert Kennedy, Shin-Ming Liu, Raymond Lo,
      + *        and Peng Tu, "A New Algorithm for Partial Redundancy Elimination
      + *        based on SSA Form", Proc. PLDI '97: 273-286, 1997.
      + * 
      + * + * NOTE: The type for all occurrences of an inserted variable is the same as the + * type of the first occurrence of the expression the variable replaces. This + * type is guaranteed since we only group expression with equal types. + */ + +/* + * Okay, you asked for it: SSAPRE. SSAPRE stands for Partial Redundency + * Elimination in Static Single-Assignment form. Partial Redundency Elimination + * invovles finding multiple occurreces of the same expressions (i.e. + * expressions that are lexically equivalent) and moving those occurrences up + * higher in the CFG so that the expression is evaluated fewer times. + * Occurrences of the expression that has been moved are replaced with a + * temporary variable that is assigned the value of the moved expression. PRE is + * a superset of Common Subexpression Elimination. + * + * The Golden Rule of SSAPRE: Move expressions to eliminate redundent + * evaluations, but do not make more work along a given path in the CFG by + * introducing an expression along a path where it originally was not. + * + * PRE techniques have been around for quite sometime, but most algorithms used + * a bit vector notation to represent occurrences of an expression. In order to + * apply those PRE techniques, the CFG in SSA form would have to be transformed + * into bit vector notation before PRE could be performed. The modified bit + * vectors would then have to be transformed back into a CFG in SSA form before + * any other optimizations (or code generation) could take place. + * + * That was the way life was until [Chow, et. al.] came along. At the 1997 PLDI + * conference, they presented a paper that showed how PRE could be performed + * directly on a CFG in SSA form. The paper itself is a rather difficult read. + * So don't feel bad if you don't understand it on the first (or seventeenth) + * read. + * + * BLOAT performs SSAPRE on the CFG for optimizable methods. Due to some of + * Java's quirks (e.g. exceptions), this implementation does not follow Chow's + * exactly. However, it is very close and understanding this implementation will + * aid the understanding of Chow and vice versa. + * + * The first step in SSAPRE is to create a worklist of all expressions that are + * candidates for SSAPRE. Only first-order expressions are entered into the + * worklist. First-order expressions consist of a single operators and two + * operands that are either a variable or have a constant value. For instance, + * a+b is a first-order expression, while a+b-c is not. The first-order + * expressions that are inserted into the worklist are "real occurrences" of the + * expression (as opposed to PHI-statements or PHI-operands, as we shall shortly + * see). In addition to real occurrences of the expression, the worklist also + * contains kill statements. Certain constructs in Java, such as exceptions, + * method calls, and synchronized blocks, limit the assumptions one can make + * about the behavior of the code. Thus, at places in the code where such + * constructs occur, kills are inserted to say "We cannot move code across this + * point." The above is performed in collectOccurrences(). + * + * SSAPRE is then performed on each expression in the worklist. The first step + * in SSAPRE is to place PHI-functions. PHI-functions are similar to the + * phi-functions that occur in converting a CFG to SSA form. Essentially, a + * PHI-function is placed at a block (node in the CFG) at which a join occurs + * (i.a. there are two paths that can enter the block), at which the expression + * is available on at least one of the incoming paths, and at which the + * expression will be used again (e.g. we don't bother putting a PHI-function at + * the "exit" node). This leads us to place PHI-functions in two situations. + * First, for each block that contains a real occurrence of the expression, a + * PHI-function is placed at every block in its iterated dominance frontier + * (DF+). Second, a PHI-function is placed in every block that contains a + * phi-function for either of the operands to the expression. At this point, the + * operands to the PHI-functions are unknown. They are of the form: + * + * h = PHI(h, h, ..., h) + * + * and are referred to as PHI-statements (also called "expression-PHIs" in + * Chow's paper). The method placePhis() inserts these hypothetical + * PHI-functions into the CFG. + * + * Once the expression-PHIs have been placed, version numbers are assigned to + * each hypothetical h. Both real occurrences of the expression and + * PHI-statements for the expression have a numbered h value associated with + * them. Occurrences that have the same version number have identical values and + * any control flow path that includes two difference h-versions must cross an + * assignment to an operand of the expression or an PHI-statement for h. The + * version numbers are assigned in two passes over the CFG. The first pass + * compiles of a worklist containing all of the real occurrences that are + * defined by an PHI-statement. It also maintains a stack of real occurrences of + * the expression. The first pass optimistically assumes that versions of the + * operands to a PHI-function are the same as the version of the expression that + * is on top of the expression stack. The second pass corrects any false + * assumptions that were made during the first pass. Since the first pass saw + * all of the occurrences, the versions of the h-variables can be determined + * from the existence of a phi-function (note lowercase phi) at that block. In + * some cases, the occurrence may be placed back into the worklist for further + * processing. The rename() method handles the assignment of version numbers + * using this two-pass method. + * + * Now that the PHI-functions have been placed and version numbers have been + * assigned, other information about the hypotheticals is extracted and will be + * used to move redundent occurrences of the expression. The first piece of + * information that is calculated is whether or not an PHI-statement is + * "down-safe" (also referred to as being "anticipated"). When version numbers + * were assigned to hypotheticals, a use-def relationship was created: + * PHI-statements use operands that are hypotheticals defined by other + * occurrences. One can conceptualize a directed graph consisting of + * PHI-statements and the directed edges going from a use of a hypothetical to + * its definition. This graph is referred to by Chow as the "SSA Graph". (This + * is not to be confused with the class SSAGraph which models the SSA Graph for + * variables, not expressions. This implementation does not directly model the + * SSA Graph.) Using the SSA Graph the down-safety of an PHI-statement is + * computed by backwards propagation along the use-def chains. An PHI-statement + * is not down-safe if there is a control flow path from that PHI-statement + * along which the expression is not evaluated (i.e. there is no real + * occurrence) before the exit block is encountered or one of the expression's + * variables is redefined. Why is down-safety important? If an PHI-statement is + * not down-safe, it is not worthwhile to hoist it any higher in the code. If it + * were to be hoisted, it might add a unnecessary evaluation of the expression + * along some path. This would break the golden rule of SSAPRE. There are two + * situations in which an PHI-statement is not down-safe. The first occurs when + * there is no path to exit along which the result (left hand side) of the + * PHI-statement is not used. The second occurs when there is a path to exit + * along which the only use of the PHI-statement result is as an operand to an + * PHI-statement which itself is not down-safe. The PHI-statement that fit the + * first criterion can be marked as not down-safe during the renaming step. In + * addition, each operand to an PHI-statement (called an PHI-operand) is marked + * as "has real use" when the path to the PHI-operand crosses a real occurrence + * of the same version of the expression. Simply put, an PHI-operand has a real + * use, if it is associated with a real expression instead of an PHI-statement. + * The down-safety of the PHI-statements in the CFG is computed using of the + * downSafety() and resetDownSafe() methods. + * + * The above description was written by Dave and Steve Lennon in early September + * of 1998. I'm surprised at how much we knew then. Consult the BLOAT Book for + * the conclusion to our exciting SSAPRE saga. + */ + +public class SSAPRE implements Optimization { + public static boolean DEBUG = false; + + public static boolean NO_THREAD = false; // Do we ignore threads? + + public static boolean NO_PRECISE = false; // Are exceptions not precise? + + public static boolean NO_ACCESS_PATHS = false; + + protected FlowGraph cfg; // CFG on which to perform SSAPRE + + protected int nextValueNumber; // Next value number to assign + + protected EditorContext context; + + protected ResizeableArrayList[] kills; + + protected boolean[] killsSorted; + + protected SideEffectChecker sideEffects; + + protected ExprWorklist worklist; // Worklist containing expr to analyze + + // Maps phi statements together as to allow for access path reduction? + protected Map phiRelated; + + public void transform(MethodState state) { + transform(state.controlFlowGraph(), state.context()); + } + + /** + * Performs SSA-based partial redundency elimination (PRE) on a control flow + * graph. + * + * @param cfg + * Control flow graph on which to perform SSA-based PRE. + * @param context + * The EditorContext containing all the classes that BLOAT knows + * about. + */ + private void transform(FlowGraph controlFlowGraph, BloatContext bloatContext) { + this.cfg = controlFlowGraph; + this.context = bloatContext; + + sideEffects = new SideEffectChecker(context); + + kills = new ResizeableArrayList[cfg.size()]; + killsSorted = new boolean[cfg.size()]; + + for (int i = 0; i < kills.length; i++) { + kills[i] = new ResizeableArrayList(); + killsSorted[i] = false; + } + + // In a single pass over the CFG: + // + // Number the expressions in each block in ascending order. + // Insert all first-order expressions into the worklist. + // Insert access path kills into the worklist. + // Insert exception throw kills into the worklist. + // Locate phi-related expressions. + // Find the next value number. + + worklist = new ExprWorklist(); + phiRelated = new LinkedHashMap(); + + // Compile the worklist of expressions on which to perform SSAPRE. + collectOccurrences(); + + // Do the transformation for each expression. + while (!worklist.isEmpty()) { + ExprInfo exprInfo = worklist.removeFirst(); + transform(exprInfo); + } + + // null these guys so that they'll be garbage collected sooner + sideEffects = null; + kills = null; + worklist = null; + } + + /** + * Performs partial redundency elimination on a given expression. This + * method is called on every lexically-distinct expression in a method. + * + * @see #collectOccurrences + * + * @see #placePhis + * @see #rename + * @see #downSafety + * @see #willBeAvail + * @see #finalize + */ + private void transform(ExprInfo exprInfo) { + if (DEBUG) { + System.out.println("PRE for " + exprInfo.prototype() + + " -------------------------"); + } + + if (exprInfo.numUses() == 0) { + if (DEBUG) { + System.out.println("Skipping...all occurrences are " + + "as targets. -------------------------"); + } + + exprInfo.cleanup(); + return; + } + + if (DEBUG) { + System.out.println("Placing Phis for " + exprInfo.prototype() + + " -------------------------"); + } + + // Place the PHI nodes for the expression. Note that these PHI nodes are + // for expressions, not variables. However, the same Phi classes are + // used. + placePhis(exprInfo); + + if (DEBUG) { + exprInfo.print(); + System.out.println("Renaming for " + exprInfo.prototype() + + " -------------------------"); + } + + // Calculate version numbers for each occurrence of the expression + // in exprInfo. Rename occurrences that have the same version number. + rename(exprInfo); + + if (DEBUG) { + exprInfo.print(); + System.out.println("Down safety for " + exprInfo.prototype() + + " -------------------------"); + } + + // Determine which PHI-nodes are "down safe". "Down safe" nodes are used + // at least once on all paths from the PHI-node to the exit node. + downSafety(exprInfo); + + if (DEBUG) { + System.out.println("Will be available for " + exprInfo.prototype() + + " -------------------------"); + } + + // Determine at which PHI-nodes the expression in exprInfo will be + // available after code insertions are performed. Code can only be + // inserted at the end of the predacessor blocks of these nodes. + willBeAvail(exprInfo); + + if (DEBUG) { + System.out.println("Finalize for " + exprInfo.prototype() + + " -------------------------"); + } + + finalize(exprInfo); + + if (DEBUG) { + System.out.println("Code motion for " + exprInfo.prototype() + + " -------------------------"); + } + + Type type = exprInfo.prototype().type(); + int v = cfg.addLocal(type); + VarExpr tmp = new LocalExpr(v, type); + + SSAConstructionInfo consInfo = new SSAConstructionInfo(cfg, tmp); + codeMotion(exprInfo, tmp, consInfo); + + if (DEBUG) { + System.out.println("Performing incremental SSA for " + + exprInfo.prototype() + " -------------------------"); + } + + // OK, this shouldn't be necessary. We should construct the SSA + // form for t as we do code motion using the expr-phis. But that was + // quite buggy in the early implementations (and probably still is), + // so I just build SSA form in another pass. If you change it to + // build SSA form for t during code motion, you must also remove + // the phis not in the IDF of the defs of t and fix up the FUD chains + // afterward. + + (new SSA()).transform(cfg, consInfo); + + // Set the value numbers for all the new exprs. + // This uses the occurrences of the var and the var-phi information + // added to consInfo by SSA construction. + + setValueNumbers(consInfo); + + // Add parents of the real occurrences to the var. + + enqueueParents(consInfo); + + if (DEBUG) { + exprInfo.print(); + System.out.println("Done with PRE for " + exprInfo.prototype() + + " -------------------------"); + } + + // Null out all the pointers in the exprInfo in case the exprInfo + // is still reachable. + exprInfo.cleanup(); + } + + /** + * Visits the CFG and for each lexically-distinct first-order expression + * whose subexpressions no have subexpressions nor side effects (that is, + * expressions containing one operatand and comprised of only local + * variables and/or constants), places all occurrences of that expression, + * sorted by their pre-order positions in the CFG, into a worklist. + * + * Note that only real occurrences of expression are inserted into the + * worklist. PHI and PHI-operand occurrences have not been placed yet. + * + * Additionally, Kill expressions are placed in the worklist to indicate + * boundaries across which code cannot be hoisted. + */ + private void collectOccurrences() { + // count represents the preorder number for each expression. It is + // assigned to each expression's key + final Int count = new Int(); + + // maxValue is the maximum value number encountered on a traversal + // of the expression tree. It is used to determine this.nextValueNumber + final Int maxValue = new Int(); + + // A Set of Blocks that begin a protected region + final Set beginTry = beginTry(); + + // Visit each node in the CFG. At each Expr node make note of the + // node's preorder number. Keep track of the largest value number + // encountered. Add Kills to the worklist when necessary. Add + // first-order real occurrences of an expression to the worklist at + // MemRefExpr (access paths) and Expr (expression) nodes. + cfg.visit(new TreeVisitor() { + public void visitBlock(Block block) { + if (beginTry.contains(block)) { + // If the block begins a protected region, then we must + // insert + // a Kill to prevent hoisting out of the region. + worklist.addKill(block, new ExceptionKill(count.value++)); + } + + block.visitChildren(this); + } + + public void visitPhiStmt(PhiStmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + stmt.visitChildren(this); + + Iterator iter = stmt.operands().iterator(); + + // Iterate over all of the operands to the phi node. + // Make special note of local (or stack) variables. + while (iter.hasNext()) { + Expr operand = (Expr) iter.next(); + + if (operand instanceof VarExpr) { + if (operand.def() != null) { + phiRelatedUnion(operand.def(), stmt.target()); + } + } + } + } + + public void visitConstantExpr(ConstantExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.setKey(count.value++); + } + + public void visitVarExpr(VarExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.setKey(count.value++); + } + + public void visitCatchExpr(CatchExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.visitChildren(this); + expr.setKey(count.value++); + worklist.addKill(expr.block(), new ExceptionKill(expr.key())); + } + + public void visitMonitorStmt(MonitorStmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + if (!NO_THREAD) { + stmt.visitChildren(this); + stmt.setKey(count.value++); + if (DEBUG) + System.out.print("MonitorStmt: "); + worklist.addKill(stmt.block(), new MemRefKill(stmt.key())); + } + } + + public void visitCallExpr(CallExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.visitChildren(this); + expr.setKey(count.value++); + if (DEBUG) + System.out.print("CallExpr: "); + worklist.addKill(expr.block(), new MemRefKill(expr.key())); + } + + public void visitMemRefExpr(MemRefExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + boolean firstOrder = isFirstOrder(expr); + + if (!firstOrder) { + expr.visitChildren(this); + } + + expr.setKey(count.value++); + + if (expr.isDef()) { + if (DEBUG) + System.out.print("MemRefExpr: "); + worklist.addKill(expr.block(), new MemRefKill(expr, expr + .key())); + } + + if (firstOrder) { + worklist.addReal(expr); + } + } + + public void visitStmt(Stmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + stmt.visitChildren(this); + } + + public void visitExpr(Expr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + if (isFirstOrder(expr)) { + worklist.addReal(expr); + } else { + expr.visitChildren(this); + } + + expr.setKey(count.value++); + } + }); + + nextValueNumber = maxValue.value + 1; + } + + /** + * Returns a Set of Blocks that begin the protected regions in the CFG. + */ + private Set beginTry() { + Set beginTry = new LinkedHashSet(); + + Iterator blocks = cfg.catchBlocks().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Handler handler = (Handler) cfg.handlersMap().get(block); + + if (handler != null) { + HashSet p = new LinkedHashSet(); + + Iterator prots = handler.protectedBlocks().iterator(); + + while (prots.hasNext()) { + Block prot = (Block) prots.next(); + p.addAll(cfg.preds(prot)); + } + + p.removeAll(handler.protectedBlocks()); + + // Add the protected region blocks which have preds outside the + // region to the beginTry set. + Iterator preds = p.iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + beginTry.addAll(cfg.succs(pred)); + } + } + } + + return beginTry; + } + + private void enqueueParents(SSAConstructionInfo consInfo) { + Set seen = new LinkedHashSet(); + + Iterator iter = cfg.nodes().iterator(); + + while (iter.hasNext()) { + Block block = (Block) iter.next(); + + Iterator e = consInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + VarExpr real = (VarExpr) e.next(); + + Node p = real.parent(); + + if (p instanceof StoreExpr && real.isDef()) { + p = p.parent(); + } + + if (p instanceof Expr && !seen.contains(p)) { + Expr expr = (Expr) p; + + seen.add(p); + + if (isFirstOrder(expr)) { + worklist.addReal(expr); + } + } + } + } + } + + private void setValueNumbers(SSAConstructionInfo consInfo) { + // Compute value numbers using the RPO algorithm \cite{Simpson96}. + // For such a small set of numbers this should be faster than + // recomputing the strongly connected components of the entire SSA + // graph and using the SCC-based algorithm. + + boolean changed = true; + + while (changed) { + changed = false; + + List postOrder = cfg.postOrder(); + ListIterator iter = postOrder.listIterator(postOrder.size()); + + while (iter.hasPrevious()) { + Block block = (Block) iter.previous(); + + PhiStmt phi = consInfo.phiAtBlock(block); + + if (phi != null) { + if (phi.target().valueNumber() == -1) { + phi.target().setValueNumber(nextValueNumber++); + changed = true; + } + + Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + VarExpr operand = (VarExpr) operands.next(); + + if (operand == null) { + continue; + } + + VarExpr def = (VarExpr) operand.def(); + + if (def == null) { + if (operand.valueNumber() == -1) { + operand.setValueNumber(nextValueNumber++); + changed = true; + } + continue; + } + + if (def.valueNumber() == -1) { + def.setValueNumber(nextValueNumber++); + changed = true; + } + + if (def.valueNumber() != operand.valueNumber()) { + operand.setValueNumber(def.valueNumber()); + changed = true; + } + } + } + + Iterator e = consInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + VarExpr real = (VarExpr) e.next(); + + if (real.isDef()) { + Assert.isTrue(real.parent() instanceof StoreExpr); + + StoreExpr store = (StoreExpr) real.parent(); + Expr rhs = store.expr(); + + if (rhs.valueNumber() == -1) { + // This should only happen with hoisted stores. + rhs.setValueNumber(nextValueNumber++); + changed = true; + } + + if (store.valueNumber() != rhs.valueNumber()) { + // This should only happen with hoisted stores. + store.setValueNumber(rhs.valueNumber()); + changed = true; + } + + if (real.valueNumber() != rhs.valueNumber()) { + real.setValueNumber(rhs.valueNumber()); + changed = true; + } + } else { + VarExpr def = (VarExpr) real.def(); + + if (def == null) { + if (real.valueNumber() == -1) { + real.setValueNumber(nextValueNumber++); + changed = true; + } + continue; + } + + if (def.valueNumber() == -1) { + // This shouldn't happen. + def.setValueNumber(nextValueNumber++); + changed = true; + } + + if (def.valueNumber() != real.valueNumber()) { + real.setValueNumber(def.valueNumber()); + changed = true; + } + } + } + } + } + + Iterator iter = cfg.nodes().iterator(); + + while (iter.hasNext()) { + Block block = (Block) iter.next(); + + PhiStmt phi = consInfo.phiAtBlock(block); + + if (phi != null) { + Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + Expr operand = (Expr) operands.next(); + + if (operand instanceof VarExpr) { + if (operand.def() != null) { + phiRelatedUnion(operand.def(), phi.target()); + } + } + } + } + } + } + + /** + * A PHI-function (different from a phi-function) is needed whenever + * different values of the same expression reach a common point in the + * program. A PHI is inserted in a block in two different situations: + * + * 1) Place PHI at the expression's iterated dominance frontier (DF+) 2) + * When there is a phi for a variable contained in the expression (this + * indicates an alteration in the expression) + * + * It is only necessary to insert a PHI at a merge point when the expression + * will occur again after that block. + */ + private void placePhis(final ExprInfo exprInfo) { + // Place Phis for each expression at the iterated dominance + // frontier of the blocks containing the expression. + + // w contains all of the blocks in which the expression occurs + ArrayList w = new ArrayList(cfg.size()); + + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + if (exprInfo.occurrencesAtBlock(block).size() > 0) { + w.add(block); + } + } + + // The iterated dominance frontier for all of the blocks containing + // the expression. Will ultimately contain all of the places at which + // PHI-function need to be inserted. + final Set df = new LinkedHashSet(cfg.iteratedDomFrontier(w)); + + // Set of phi functions for the variables in the expression + final ArrayList worklist = new ArrayList(); + + // Set of phi functions that have ever been added to the worklist. + // When blocks in the worklist are processed, they are removed. + // inWorklist ensures that a block is not processed more than once. + final Set inWorklist = new LinkedHashSet(); + + // For each variable occurrence in exprInfo, place a Phi where + // there is a phi for the variable. + + blocks = cfg.nodes().iterator(); + + // Iterate over every block in the method and make a worklist of all + // phi functions that define one of the variables in this expression. + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Iterator e = exprInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + Expr real = (Expr) e.next(); + + real.visit(new TreeVisitor() { + public void visitVarExpr(VarExpr var) { + Expr def = var.def(); + + if (def == null) { + return; + } + + Node p = def.parent(); + + if (p instanceof PhiStmt && !inWorklist.contains(p)) { + worklist.add(p); + inWorklist.add(p); + } + } + }); + } + } + + // Go through the worklist and add the blocks containing the + // phi-functions to list of blocks to which to add PHI-functions. + // Also, examine each operand to the phi-function and add it to + // the worklist if it itself is defined by a phi-function. + while (!worklist.isEmpty()) { + PhiStmt phi = (PhiStmt) worklist.remove(worklist.size() - 1); + df.add(phi.block()); + + Iterator iter = phi.operands().iterator(); + + while (iter.hasNext()) { + Expr expr = (Expr) iter.next(); + + if (expr instanceof VarExpr) { + VarExpr var = (VarExpr) expr; + + Expr def = var.def(); + + if (def == null) { + continue; + } + + Node p = def.parent(); + + if (p instanceof PhiStmt && !inWorklist.contains(p)) { + worklist.add(p); + inWorklist.add(p); + } + } + } + } + + // df contains all of the blocks in which an PHI-statement for the + // expression should be added. Add them to the exprInfo. + Iterator iter = df.iterator(); + + while (iter.hasNext()) { + Block block = (Block) iter.next(); + exprInfo.addPhi(block); + } + } + + /** + * Rename all occurrences of the expression. placePhis went through the CFG + * and placed PHI-functions at merge blocks in the code. Now, we have to go + * through and assign version numbers to all of the "h" variables generated + * by the PHI-functions. + *

      + * There are two methods outlined in [Chow 1997]. The first is more + * straightforward (ya right, it took us two days to figure it out) while + * the second is more space efficient. The second method delays the renaming + * of the "h" variables. It makes two passes over the CFG (actually, they + * are preorder traversals of the dominator tree). + *

      + * The first pass builds a worklist containing all of the real occurrences + * that are defined by a PHI for a given expression. We optimisitically + * assume that versions of PHI operands are the same as the version on top + * of the expression stack. These assumptions are checked for correctness + * during the second pass. + *

      + * The second pass performs the correct renaming. It relies on seeing a + * later occurrence of the expression. That is, it implies that at the + * earlier PHI, the expression is partially anticipated. The second pass + * operates on all of the real occurrences in the worklist built in the + * first pass. From the versions of the variables at the merge block of a + * PHI, the versions of the variables at each predacessor block are + * determined based on the presence or absence of a phi-function for the at + * that merge block. If the versions are different from the assumed versions + * from the first pass, the operand is rest to null (bottom). Otherwise, the + * operand is correct. If the PHI operand is also defined by a PHI, it is + * added to the worklost and is handled later. + */ + // Rename all occurrences of the expression. This is done in two passes. + // + // The first pass assigns version numbers (FUD chain pointers really) in + // a + // pre-order traversal of the dominator tree and builds the worklist for + // pass 2. + // + // We optimistically assume that a Phi can be used as a definition for a + // real and clean up in pass 2 by adjusting all the FUD chains if the + // assumption proves false. + // + // NOTE: Renaming is where almost all previous PRE bugs have come from, + // so + // when looking for a bug, it might be good to start looking here first. + private void rename(ExprInfo exprInfo) { + // Renaming pass 1. This assigns version numbers (FUD chain + // pointers really) in a pre-order traversal of the dominator tree + // and builds the worklist for pass 2. + + ArrayList renameWorklist = new ArrayList(); + + search(cfg.source(), exprInfo, null, null, renameWorklist); + + // Pass 2. + + // First, build another worklist which uses the leaves of the reals + // on the old worklist. We extend this worklist later with the leaves + // factored through var-phis. + + HashSet seen = new LinkedHashSet(); + + LinkedList leavesWorklist = new LinkedList(); + + Iterator iter = renameWorklist.iterator(); + + while (iter.hasNext()) { + // Examine each real occurrence that may need more work. + // Construct a list of the operands of the real occurrence. We + // should have already determined that the occurrence is first + // order. So, if we hit anything other than a constant, local + // variable, or stack expression, we have a problem. + + final Expr real = (Expr) iter.next(); + final Phi phi = (Phi) exprInfo.def(real); + + // Keep track of operands of the real occurrence. + final ArrayList leaves = new ArrayList(); + + real.visitChildren(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + // This should have been checked before adding + // the real to the worklist. + throw new RuntimeException(); + } + + public void visitConstantExpr(ConstantExpr expr) { + leaves.add(expr); + } + + public void visitVarExpr(VarExpr expr) { + leaves.add(expr.def()); + } + + public void visitExpr(Expr expr) { + throw new RuntimeException(); + } + }); + + // Save the leaves for later use when building phi operands. + phi.setLeaves(leaves); + + leavesWorklist.add(phi); + } + + // Now we actually go about assigning version numbers to the + // operands of the PHI-statement. If the operand is defined by a + // real occurrence (RealDef), examine the children of the real + // occurrence. + + while (!leavesWorklist.isEmpty()) { + final Phi phi = (Phi) leavesWorklist.removeFirst(); + phi.setLive(true); + + final List leaves = phi.leaves(); + + // Compare the leaves against what we expect for the Phi + // operands. + Iterator preds = cfg.preds(phi.block()).iterator(); + + PREDS: while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + final Def operand = phi.operandAt(pred); + + if (operand instanceof RealDef) { + Expr expr = ((RealDef) operand).expr; + + final Bool match = new Bool(); + match.value = true; + + final Iterator leafIter = leaves.iterator(); + + expr.visitChildren(new TreeVisitor() { + public void visitExpr(Expr expr) { + throw new RuntimeException(); + } + + public void visitStoreExpr(StoreExpr expr) { + expr.target().visit(this); + } + + public void visitConstantExpr(ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(Expr expr) { + if (!leafIter.hasNext()) { + // We've already examined all of the leaves, + // they + // don't match + match.value = false; + return; + } + + Expr leaf = (Expr) leafIter.next(); + + // Factor the leaf through any var-phis there. + // That is, + // If the leaf is defined by a phi-statement, + // use the + // corresponding phi-operand as the leaf. If the + // leaves + // don't match (i.e. are not constants, + // variables, nor + // have the same value number), say so. + if (leaf instanceof VarExpr) { + Assert.isTrue(((VarExpr) leaf).isDef()); + + if (leaf.parent() instanceof PhiJoinStmt) { + PhiJoinStmt leafPhi = (PhiJoinStmt) leaf + .parent(); + + if (leafPhi.block() == phi.block()) { + leaf = leafPhi.operandAt(pred); + } + } + } + + if (!(leaf instanceof ConstantExpr) + && !(leaf instanceof VarExpr)) { + + match.value = false; + return; + } + + if (expr.valueNumber() != leaf.valueNumber()) { + match.value = false; + return; + } + } + }); + + if (!match.value || leafIter.hasNext()) { + // If the leaves do not match (or if we didn't get to + // all + // of the leaves), then we have a null PHI-operand + // (bottom) and the operand does not have a real use. + phi.setOperandAt(pred, null); + phi.setHasRealUse(pred, false); + } + + } else if (operand instanceof Phi) { + ArrayList newLeaves = new ArrayList(leaves.size()); + Phi opPhi = (Phi) operand; + + Iterator leafIter = leaves.iterator(); + + // If the operand is defined by a PHI-statement, + + LEAVES: while (leafIter.hasNext()) { + Expr leaf = (Expr) leafIter.next(); + + // Factor the leaf through a phi. + if (leaf instanceof VarExpr) { + Assert.isTrue(((VarExpr) leaf).isDef()); + + if (leaf.parent() instanceof PhiJoinStmt) { + PhiJoinStmt leafPhi = (PhiJoinStmt) leaf + .parent(); + + if (leafPhi.block() == phi.block()) { + leaf = leafPhi.operandAt(pred); + } + } + } + + if (leaf instanceof VarExpr) { + leaf = leaf.def(); + + if (leaf.block() == opPhi.block()) { + if (leaf.parent() instanceof PhiJoinStmt) { + newLeaves.add(leaf); + continue LEAVES; + } + } else if (leaf.block().dominates(opPhi.block())) { + newLeaves.add(leaf); + continue LEAVES; + } + } + + // The leaf is defined after the operand. + phi.setOperandAt(pred, null); + phi.setHasRealUse(pred, false); + continue PREDS; + } + + Assert.isTrue(leaves.size() == newLeaves.size()); + + // If we got here, the real only uses leaves defined + // above + // the operand. Add the operand to the worklist. + Pair pair = new Pair(phi, opPhi); + + if (!seen.contains(pair)) { + seen.add(pair); + opPhi.setLeaves(newLeaves); + leavesWorklist.add(opPhi); + } + } + } + } + + // Remove the dead phis. + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null && !phi.live()) { + if (DEBUG) { + System.out.println(" dead Phi at " + block); + } + + exprInfo.removePhi(block); + } + } + } + + class Pair { + Object a; + + Object b; + + Pair(Object a, Object b) { + this.a = a; + this.b = b; + } + + public boolean equals(Object o) { + return o instanceof Pair && ((Pair) o).a.equals(a) + && ((Pair) o).b.equals(b); + } + + public int hashCode() { + return a.hashCode() ^ b.hashCode(); + } + } + + /** + * This method performs the first pass of the delayed renaming algorithm. + * Recall that the original renaming algorithm kept a stack for the + * "current" version number of each variable used in the expression being + * renamed. Well, when a real occurrence of the expression is encountered, + * we don't need the stacks because the real occurrence contains the current + * version numbers of the variables. So, we only need the version numbers + * when renaming the "h" variables for PHI-operands. + * + * When this first pass encounters a PHI-operand, it optimistically assumes + * that the version on top of the stack is the correct version. The "h" + * values for real occurrences will be handled correctly. + * + * This implementation represents an occurrences "h" value by its Def (the + * "setDef" method of ExprInfo). This method performs a pre-order traversal + * of the CFG's dominator tree and assigns "names" (actually references to + * Defs) to occurrences of an expression. + * + * The end result of this traversal is a worklist of real occurrences that + * require further renaming. Along the way, we compute the down safety (or + * lack there of) of some PHI-statements. + * + * @param block + * The block in the CFG being traversed + * @param exprInfo + * The expression on which we are performing PRE. + * @param top + * The most recently encountered real occurrence of the + * expression. It can be thought of as the "top" of a stack of + * expressions. + * @param topDef + * top's Def. That is, its "h" value. + */ + // This pass is pretty much as described in Chow97 in the Delayed + // Renaming section, except we have to handle kills for access paths + // and for exceptions. + // + // Instead of using an explicit stack of occurrences, top points to + // the occurrence at the top of the stack and topdef points to top's + // def. top is null if topdef is a Phi and a real occurrence hasn't + // followed it. Thus a Phi is not down safe if it is killed and top + // is null. + private void search(Block block, ExprInfo exprInfo, Expr top, Def topdef, + List renameWorklist) { + if (DEBUG) { + System.out.println(" renaming in " + block); + } + + Phi phi = exprInfo.exprPhiAtBlock(block); + + // If there's a PHI in the block, make this PHI the new topdef. + // + if (phi != null) { + + top = null; + topdef = phi; + + // If the expression has a stack variable, don't allow any + // hoisting. + // + // To prevent hoisting, it is sufficient to make the phi not + // down safe. If any operand of the phi is null and the phi is + // not down safe, no hoisting will be attempted (see + // canBeAvail). If an operand is non-null, then the expression + // is already evaluated on that path and no hoisting should be + // attempted. + + if (exprInfo.hasStackVariable()) { + phi.setDownSafe(false); + } + + // If the expression can throw an exception, don't allow any + // hoisting. This is stricter than it should be. + // + // We can fix this for fields, divisions, and remainders with + // a trick like: NullCheck(p).f or x / ZeroCheck(y). + // + // Array refs are more complicated since you need both the + // array and the index checked. + // + // Don't bother if exceptions are not precise. + + if (!NO_PRECISE && exprInfo.hasSideEffects()) { + phi.setDownSafe(false); + } + } + + // If we hit the sink node, a phi at the top of the stack is not + // down safe. + if (block == cfg.sink()) { + if (topdef instanceof Phi && top == null) { + ((Phi) topdef).setDownSafe(false); + } + + // The sink node has no real occurrences and no children in + // the dominator tree. So, go home. + return; + } + + // Kill (nullify) topdef in catch blocks. This prevents hoisting into + // protected regions. + if (cfg.catchBlocks().contains(block)) { + if (topdef instanceof Phi && top == null) { + ((Phi) topdef).setDownSafe(false); + } + + if (DEBUG) { + System.out.println("Top killed at catch " + block); + } + + top = null; + topdef = null; + } + + // Go through all of the real occurrences (and any kills) in the + // block in the order that they appear. + Iterator e = exprInfo.occurrencesAtBlock(block).iterator(); + + while (e.hasNext()) { + Object obj = (Object) e.next(); + + if (obj instanceof Kill) { + if (topdef != null) { + Kill kill = (Kill) obj; + + if (DEBUG) { + System.out.println("Kill " + kill.expr); + } + + boolean die = false; + + // If we have a memory reference (access path), we need + // to + // check if the Kill could be an alias def for this + // expression. + if (exprInfo.prototype() instanceof MemRefExpr) { + if (kill instanceof MemRefKill) { + MemRefExpr k = (MemRefExpr) kill.expr; + MemRefExpr p = (MemRefExpr) exprInfo.prototype(); + + if (kill.expr == null) { + // If kill.expr is null, kill everything. + die = true; + + } else if (TBAA.canAlias(context, k, p)) { + die = true; + } + } + } + + // If we haven't been killed yet, see if the kill is + // there + // to prevent us from hoisting out of protected regions. + // + // This is possibly not necessary since if the exception + // can be thrown outside the protected region, we won't + // get + // here in the first place. Removing this code could + // give + // us better results. + if (!die && exprInfo.hasSideEffects()) { + if (kill instanceof ExceptionKill) { + // Just a kill to keep us from hoisting out of + // a protected region or out of a handler. + die = true; + } + } + + if (die) { + if (DEBUG) { + System.out.println("Killed"); + } + + if (topdef instanceof Phi && top == null) { + ((Phi) topdef).setDownSafe(false); // Can't use it + } + + top = null; + topdef = null; + } + } + + continue; + } + + // If we get here, we are dealing with a real occurrence of the + // expression. Now we need to determine whether or not the real + // occurrence matches the "h" value (definition) on top of the + // "stack" (topdef). + Expr real = (Expr) obj; + + // If the real has a store in it, we can't reuse the def at + // the top of stack, even if it is a Phi. Because something + // got redefined inside the expression. + final Bool hasStore = new Bool(); + + if (real.isDef()) { + hasStore.value = true; + + } else { + real.visit(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + hasStore.value = true; + } + + public void visitExpr(Expr expr) { + if (!hasStore.value) { + expr.visitChildren(this); + } + } + }); + } + + boolean matches = true; + + if (hasStore.value) { + matches = false; + + if (DEBUG) { + System.out.println("real has store"); + } + } + + if (matches && topdef == null) { + matches = false; + + if (DEBUG) { + System.out.println("null topdef"); + } + } + + if (matches && topdef instanceof Phi) { + if (!matchesPhi(real, (Phi) topdef)) { + // Some variable used in the real got redefined after + // the + // PHI. So they'll have different values. + matches = false; + + if (DEBUG) { + System.out.println("uses var defined after topdef"); + } + } + } + + if (matches && top != null) { + if (!matches(top, real)) { + matches = false; + + if (DEBUG) { + System.out.println("mismatch " + top + " != " + real); + } + } + } + + // If topdef does not match the real occurrence, then make the + // real + // occurrence the new topdef. + if (!matches) { + if (top == null && topdef instanceof Phi) { + // No real occurrence after the Phi, so the Phi is not + // down safe. + ((Phi) topdef).setDownSafe(false); + } + + // We know that the real occurrence defines the expression + RealDef def = new RealDef(real); + exprInfo.setDef(real, def); + topdef = def; + + } else { + // The operands of the real occurrence and the PHI-statement + // match. So, the definition on top of the "stack" defines + // the expression. + + Assert.isTrue(topdef != null); + + if (DEBUG) { + System.out.println("copying top def"); + } + + if (topdef instanceof Phi) { + // If the definition on top of the renaming stack is a + // PHI-statement, the real occurrence may need more + // work. + // Add it to the renameWorklist. + renameWorklist.add(real); + } + + // Copy the definition from the top of the stack. + exprInfo.setDef(real, topdef); + } + + top = real; + } + + Iterator succs = cfg.succs(block).iterator(); + + // Examine each successor block of the block being traversed. If + // the block contains a PHI-statement, set the PHI-statement's + // operand corresponding to the block being traversed to the + // definition on top of the renaming stack. + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + // If we hit the sink node, a Phi at the top of the stack is not + // down safe. + if (succ == cfg.sink()) { + if (top == null && topdef instanceof Phi) { + ((Phi) topdef).setDownSafe(false); + } + } + + Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + if (succPhi != null) { + succPhi.setOperandAt(block, topdef); + + if (top != null) { + succPhi.setHasRealUse(block, true); + } + } + } + + Iterator children = cfg.domChildren(block).iterator(); + + // Visit each child in the dominator tree. + while (children.hasNext()) { + Block child = (Block) children.next(); + search(child, exprInfo, top, topdef, renameWorklist); + } + } + + /** + * This method determines whether or not a given (real occurrence of an) + * expression has the same operands as the target of a PHI-statement. That + * is, has one of the operands of the real occurrence changed since the the + * PHI-statement? + */ + private boolean matchesPhi(final Expr real, final Phi phi) { + final Bool match = new Bool(); + match.value = true; + + real.visitChildren(new TreeVisitor() { + public void visitExpr(Expr expr) { + if (match.value) { + expr.visitChildren(this); + } + } + + public void visitStoreExpr(StoreExpr expr) { + // A store means a new SSA number, so they won't match + match.value = false; + } + + public void visitVarExpr(VarExpr expr) { + if (!match.value) { + return; + } + + // We're dealing with one of the operands of the real + // occurrence. If the operand is defined by a phi-statement + // that occurrs in the same block as the PHI-statement, then + // the variable in the real occurrence is the same as that in + // the PHI-statement. Similarly, is the block in which the + // real occurrence's definition occurs dominate the block in + // which the PHI-statement occurs, the two versions of the + // variable are the same. Otherwise the variable has been + // modified between the PHI-statement and the real occurrence. + + VarExpr def = (VarExpr) expr.def(); + + if (def == null) { + match.value = false; + return; + } + + Block block = phi.block(); + + Node p = def.parent(); + + if (block == p.block()) { + // Anything other than a var-phi means the real + // occurrence + // uses a variable defined after the Phi. + if (p instanceof PhiJoinStmt) { + return; + } + + } else if (p.block().dominates(block)) { + // The real uses a var defined above the phi. + // This, too, is okay. + return; + } + + // The real uses a variable defined after the Phi. + match.value = false; + } + }); + + return match.value; + } + + /** + * Compares two expressions and determines whether or not they match. + */ + private boolean matches(final Expr expr1, final Expr expr2) { + final LinkedList leaves = new LinkedList(); + + expr1.visit(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + expr.target().visit(this); + } + + public void visitConstantExpr(ConstantExpr expr) { + leaves.add(expr); + } + + public void visitVarExpr(VarExpr expr) { + leaves.add(expr); + } + }); + + final Bool match = new Bool(); + match.value = true; + + expr2.visit(new TreeVisitor() { + public void visitExpr(Expr expr) { + if (match.value) { + expr.visitChildren(this); + } + } + + public void visitStoreExpr(StoreExpr expr) { + if (match.value) { + expr.target().visit(this); + } + } + + public void visitConstantExpr(ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(Expr expr) { + if (leaves.isEmpty()) { + match.value = false; + return; + } + + Expr leaf = (Expr) leaves.removeFirst(); + + if (leaf == null || expr.valueNumber() != leaf.valueNumber()) { + match.value = false; + } + } + }); + + return match.value; + } + + private Expr buildPhiOperand(ExprInfo exprInfo, final Phi phi, + final Block pred) { + final Iterator leaves = phi.leaves().iterator(); + + Expr expr = (Expr) exprInfo.prototype().clone(); + + expr.visit(new TreeVisitor() { + public void visitExpr(StoreExpr expr) { + throw new RuntimeException(); + } + + public void visitConstantExpr(ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(Expr expr) { + if (leaves.hasNext()) { + Expr leaf = (Expr) leaves.next(); + + if (leaf instanceof VarExpr) { + Assert.isTrue(((VarExpr) leaf).isDef()); + + if (leaf.parent() instanceof PhiJoinStmt) { + PhiJoinStmt leafPhi = (PhiJoinStmt) leaf.parent(); + + if (leafPhi.block() == phi.block()) { + leaf = leafPhi.operandAt(pred); + + if (leaf instanceof VarExpr) { + leaf = leaf.def(); + } + } + } + } + + Assert.isTrue(leaf != null); + + Expr copy = (Expr) leaf.clone(); + + if (leaf.isDef()) { + copy.setDef((VarExpr) leaf); + } + + expr.replaceWith(copy); + } else { + throw new RuntimeException(); + } + } + }); + + expr.setValueNumber(nextValueNumber++); + + return expr; + } + + /** + * A Phi is not down safe if there is a control flow path from that Phi + * along which the expression is not evaluated before exit or being altered + * by redefinition of one of the variables of the expression. This can + * happen if: + * + * 1) There is a path to exit along which the Phi target is not used. 2) + * There is a path to exit along which the Phi target is used only as the + * operand of a non-down-safe Phi. + */ + private void downSafety(ExprInfo exprInfo) { + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null || phi.downSafe()) { + continue; + } + + Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + resetDownSafe(phi, pred); + } + } + } + + private void resetDownSafe(Phi phi, Block block) { + if (phi.hasRealUse(block)) { + return; + } + + Def def = phi.operandAt(block); + + if (def instanceof Phi) { + Phi phidef = (Phi) def; + + if (phidef.downSafe()) { + phidef.setDownSafe(false); + + if (DEBUG) { + System.out.println(" def = Phi in " + + phidef.block()); + System.out.println(" def made not down safe"); + } + + Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + resetDownSafe(phidef, pred); + } + } + } + } + + /** + * Determines whether or not a PHI expression is "will be available". Will + * be available determines where we end up placing an evaluation of the + * expression. Will be available = Can be available AND (not Later) + */ + private void willBeAvail(ExprInfo exprInfo) { + computeCanBeAvail(exprInfo); + computeLater(exprInfo); + } + + /** + * Can be available (cba) means "at this point, we can insert an evaluation + * of the expression". If cba = 0, then the PHI-statement is "useless" and + * uses of it are changed to tack (bottom). Can be available depends on the + * down safety of the PHI-statement. + */ + private void computeCanBeAvail(ExprInfo exprInfo) { + Iterator blocks = cfg.nodes().iterator(); + + // Go through every PHI-statement of the exprInfo. + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (!phi.canBeAvail()) { + continue; + } + + if (phi.downSafe()) { + continue; + } + + Iterator e = cfg.preds(block).iterator(); + + // We determined above that: + // 1. This PHI-statement is not down safe + // 2. It is currently can be available + // Now, if one of the PHI-statement's operands is tack (null), + // reset "can be avail" for this PHI-statement. + + while (e.hasNext()) { + Block pred = (Block) e.next(); + + Def operand = phi.operandAt(pred); + + if (operand == null) { + resetCanBeAvail(exprInfo, phi); + break; + } + } + } + } + + /** + * Resets the cba flag for a given PHI-expression and then iterates over the + * PHI-statement's operands and resets them under certain conditions. + */ + private void resetCanBeAvail(ExprInfo exprInfo, Phi phi) { + phi.setCanBeAvail(false); + + Iterator blocks = cfg.nodes().iterator(); + + // Iterate over every PHI-statement, other, that uses the + // the "h" defined by phi as an operand... + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + + Def operand = other.operandAt(pred); + + // For each use of the "h" defined by exprInfo... + if (operand == phi) { + if (other.hasRealUse(pred)) { + continue; + } + + // If the use does not have a real use, set the use to + // tack (bottom). + other.setOperandAt(pred, null); + + // Since we changed other (by setting one of its + // operands to + // tack), if other is not down safe, propagate this + // information + // back up the CFG by resetting its cba. + if (!other.downSafe() && other.canBeAvail()) { + resetCanBeAvail(exprInfo, other); + } + } + } + } + } + + /** + * Later basically says, "We cannot place an evaluation of the expression + * any later that this point without adding additional evaluation(s) along + * some path". An expression is "interesting" when later is false. + */ + private void computeLater(ExprInfo exprInfo) { + Iterator blocks = cfg.nodes().iterator(); + + // Initialize later to can be available... + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + phi.setLater(phi.canBeAvail()); + } + + blocks = cfg.nodes().iterator(); + + // Iterate over each PHI-statement, phi... + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (!phi.later()) { + continue; + } + + Iterator e = cfg.preds(block).iterator(); + + // If later == true and there is an operand of phi that: + // 1. is not tack + // 2. has a real use + // set later to false and propagate this information back up the + // CFG. + // Basically, what we're saying is that if an operand of the + // PHI-statement has a real use, we want to evaluate the + // expression + // now. + + while (e.hasNext()) { + Block pred = (Block) e.next(); + Def operand = phi.operandAt(pred); + + if (operand != null && phi.hasRealUse(pred)) { + resetLater(exprInfo, phi); + break; + } + } + } + } + + /** + * Resets later and propagates this information back up the CFG. + */ + private void resetLater(ExprInfo exprInfo, Phi phi) { + phi.setLater(false); + + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + Iterator e = cfg.preds(block).iterator(); + + // For PHI-statement that has the "h" defined by phi as an + // operand... + + while (e.hasNext()) { + Block pred = (Block) e.next(); + Def operand = other.operandAt(pred); + + if (operand == phi) { + if (!other.later()) { + continue; + } + + // Propagate later = false up the CFG. + resetLater(exprInfo, other); + break; + } + } + } + } + + /** + * Finalize is the final step in preparing for the placement of temporaries + * and evaluations of the expression. It decides whether the results of real + * occurrences should be computed on the spot (and saved to a temporary) or + * reloaded from a temporary. Some PHI-statements are removed and some are + * replaced by PHI-statements operating on the temporaries. Additional + * evaluations of the expression may be added where the expression is not + * available. + */ + private void finalize(ExprInfo exprInfo) { + // We assume that all availDef for exprInfo are tack. + // Perform a perorder traversal of the dominance tree. Remember that + // the root of the dominance tree is also the root of the CFG. + finalizeVisit(exprInfo, cfg.source(), null); + } + + /** + * + * + * @param exprInfo + * The expression on which we're performing SSAPRE. + * @param block + * The block to search for occurrences of exprInfo. + * @param top + * Top is used to determine when an element of Avail_def + * dominates a given occurrence. + */ + private void finalizeVisit(final ExprInfo exprInfo, Block block, Def top) { + if (DEBUG) { + System.out.println(" finalizing " + block); + } + + // Get the (only) PHI-statement at the current block. If wba = 1 for + // the PHI-statement, + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + if (phi.willBeAvail()) { + exprInfo.setAvailDef(phi, phi); + top = phi; + } else { + top = null; + } + } + + Iterator reals = exprInfo.realsAtBlock(block).iterator(); + + // Iterate over all of the real occurrences in the block. + + while (reals.hasNext()) { + Expr real = (Expr) reals.next(); + + if (DEBUG) { + System.out.println(" -----------"); + } + + // Get defining "h" occurrence for the expression + Def def = exprInfo.def(real); + Assert.isTrue(def != null, real + " is undefined"); + + // Get Avail_def[i][x] + Def availDef = exprInfo.availDef(def); + + // If Avail_def[i][x] == bottom (tack) + // or Avail_def[i][x] does not dominate this occurrence of E[i] + // Avail_def[i][x] = this occurrence of E[i] + // + // The statement (availDef != top) is equivalent to saying + // "availDef + // does not dominate real". Why is this so? Top essentially + // keeps + // track of the last PHI-statement we've seen. Thus, top will + // only + // be changed when we encounter a PHI-statement. We only + // encounter + // PHI-statements at join blocks, which are obviously not + // dominated + // by a block (containing availDef) along one of its paths. + if (availDef == null || availDef != top) { + top = new RealDef(real); + exprInfo.setAvailDef(def, top); + } + // If the available definition is a real occurrence, set its + // save and reload flags + else if (availDef instanceof RealDef) { + exprInfo.setReload(real, true); + exprInfo.setSave(((RealDef) availDef).expr, true); + } else { + Assert.isTrue(availDef instanceof Phi); + exprInfo.setReload(real, true); + } + } + + Iterator succs = cfg.succs(block).iterator(); + + // Iterate over each successor block in the CFG... + while (succs.hasNext()) { + Block succ = (Block) succs.next(); + + Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + // If the PHI-statement is will be available, + if (succPhi != null) { + if (succPhi.willBeAvail()) { + if (succPhi.canInsert(block)) { + succPhi.setSaveOperand(block, true); + } else { + Def operand = succPhi.operandAt(block); + + Assert.isTrue(operand != null); + + Def availDef = exprInfo.availDef(operand); + + if (availDef instanceof RealDef) { + exprInfo.setSave(((RealDef) availDef).expr, true); + } + } + } + } + } + + Iterator children = cfg.domChildren(block).iterator(); + + while (children.hasNext()) { + Block child = (Block) children.next(); + finalizeVisit(exprInfo, child, top); + } + } + + private void codeMotion(ExprInfo exprInfo, VarExpr tmp, + SSAConstructionInfo consInfo) { + List[] targets = new List[cfg.size()]; + + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + + Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + + if (!phi.saveOperand(pred)) { + continue; + } + + Expr operand = buildPhiOperand(exprInfo, phi, pred); + Assert.isTrue(operand != null); + + VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(operand.valueNumber()); + + StoreExpr store = new StoreExpr(t, operand, t.type()); + store.setValueNumber(operand.valueNumber()); + pred.tree().addStmtBeforeJump(new ExprStmt(store)); + + if (DEBUG) { + System.out.println("Created new store: " + store); + } + + // Save the target for later since we need to add + // it to consInfo last. + int predIndex = cfg.preOrderIndex(pred); + + if (targets[predIndex] == null) { + targets[predIndex] = new ArrayList(); + } + + targets[predIndex].add(t); + + if (DEBUG) { + System.out.println("insert at end of " + pred + ": " + + store); + } + } + } + + Iterator e = exprInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + Expr real = (Expr) e.next(); + + if (exprInfo.save(real)) { + if (!real.isDef()) { + save(exprInfo, tmp, real, consInfo); + } else { + saveTarget(exprInfo, tmp, real, consInfo); + } + + } else if (exprInfo.reload(real)) { + Assert.isFalse(real.isDef(), "Can't reload a def: " + real + + " in " + real.parent()); + reload(exprInfo, tmp, real, consInfo); + } + } + } + + blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + Block block = (Block) blocks.next(); + int blockIndex = cfg.preOrderIndex(block); + + if (targets[blockIndex] != null) { + Iterator iter = targets[blockIndex].iterator(); + + while (iter.hasNext()) { + VarExpr t = (VarExpr) iter.next(); + consInfo.addReal(t); + } + } + } + } + + private void save(ExprInfo exprInfo, VarExpr tmp, Expr real, + SSAConstructionInfo consInfo) { + if (DEBUG) { + System.out.println("SAVE: " + real + " to " + tmp + + "--------------------------------"); + } + + if (real instanceof CheckExpr && exprInfo.hasStackVariable()) { + // Check(x) leaves x on the stack. Do nothing. + return; + } + + // Replace expression + // use x + e + // with + // use x + (t := e) + // We must evaluate x before e. + + Node parent = real.parent(); + VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(real.valueNumber()); + + StoreExpr store = new StoreExpr(t, real, real.type()); + store.setValueNumber(real.valueNumber()); + parent.visit(new ReplaceVisitor(real, store)); + + consInfo.addReal(t); + + if (DEBUG) { + System.out.println("END SAVE--------------------------------"); + } + } + + private void reload(ExprInfo exprInfo, VarExpr tmp, Expr real, + SSAConstructionInfo consInfo) { + if (DEBUG) { + System.out.println("RELOAD: " + real + " to " + tmp + + "--------------------------------"); + } + + Expr t; + + if (real instanceof CheckExpr && exprInfo.hasStackVariable()) { + // Check(x) leaves x on the stack. Replace with just x. + + t = ((CheckExpr) real).expr(); + real.parent().visit(new ReplaceVisitor(real, t)); + real.cleanupOnly(); + + } else { + // Replace + // use e + // with + // use t + + t = (VarExpr) tmp.clone(); + t.setValueNumber(real.valueNumber()); + real.replaceWith(t); + + consInfo.addReal((VarExpr) t); + } + + if (DEBUG) { + System.out.println("reload t " + t + " in " + t.parent()); + } + + if (DEBUG) { + System.out.println("END RELOAD--------------------------------"); + } + } + + private void saveTarget(ExprInfo exprInfo, VarExpr tmp, Expr real, + SSAConstructionInfo consInfo) { + if (DEBUG) { + System.out.println("SAVE TARGET: " + real + " to " + tmp + + "--------------------------------"); + } + + Assert.isTrue(real instanceof MemRefExpr); + + // Replace + // a.b := c + // with: + // a.b := (t := c); + + VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(real.valueNumber()); + + StoreExpr store = (StoreExpr) real.parent(); + Expr rhs = store.expr(); + + StoreExpr rhsStore = new StoreExpr(t, rhs, rhs.type()); + rhsStore.setValueNumber(real.valueNumber()); + store.visit(new ReplaceVisitor(rhs, rhsStore)); + + consInfo.addReal(t); + + if (DEBUG) { + System.out.println("save target " + store); + } + + if (DEBUG) { + System.out.println("END SAVE TARGET------------------------------"); + } + } + + /** + * Returns whether or not an expression is first-order. A first-order + * expression has only one operator. For example, a+b is first-order. + */ + boolean isFirstOrder(Expr expr) { + FirstOrderChecker f = new FirstOrderChecker(); + expr.visit(f); + return f.firstOrder; + } + + /** + * FirstOrderChecker is a TreeVistor that traverses an expression tree and + * determines whether or not it is first order. A first order expression + * Override all visitXXXExpr methods so that they do not visit children. We + * only want to check the expr we first visit. + */ + private final class FirstOrderChecker extends TreeVisitor { + boolean firstOrder = false; + + public void visitExpr(Expr expr) { + } + + /** + * A leaf in the expression tree consists of an expression that only + * references local variables, or an expression that is a constant, or + * an expressions that stores into a local variable. + */ + private boolean isLeaf(Expr expr) { + if (expr instanceof StoreExpr) { + return ((StoreExpr) expr).target() instanceof LocalExpr; + } + + return expr instanceof LocalExpr || expr instanceof ConstantExpr; + } + + public void visitCheckExpr(CheckExpr expr) { + // UGLY: We special case RC and UC to allow stack variables + // since + // they do not change the operand stack at all. However, since + // they do contain stack variables, we cannot hoist these + // expressions, but we can one eliminate them so long as they + // are replaced with stack variables rather than locals. + + if (isLeaf(expr.expr()) || expr.expr() instanceof StackExpr) { + firstOrder = true; + } + } + + /** + * An arithmetic expression is first-order if both its left and right + * operands are leaves. + */ + public void visitArithExpr(ArithExpr expr) { + if (isLeaf(expr.left()) && isLeaf(expr.right())) { + firstOrder = true; + } + } + + /** + * An ArrayLengthExpr is first-order when the array whose length is + * being taken is expressed as a leaf. + */ + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + if (isLeaf(expr.array())) { + firstOrder = true; + } + } + + /** + * An ArrayRefExpr is first order when both the array it references and + * the index used to reference it are expressed as leaves. + */ + public void visitArrayRefExpr(ArrayRefExpr expr) { + if (NO_ACCESS_PATHS) { + return; + } + + if (isLeaf(expr.array()) && isLeaf(expr.index())) { + firstOrder = true; + } + } + + public void visitCastExpr(CastExpr expr) { + if (isLeaf(expr.expr())) { + firstOrder = true; + } + } + + /** + * If a field is volatile (meaning that a field may be changed by other + * threads), a reference to it is not first order. I'm not too sure why + * this makes any difference. + */ + public void visitFieldExpr(FieldExpr expr) { + if (NO_ACCESS_PATHS) { + return; + } + + if (isLeaf(expr.object())) { + try {// WILL CAUSE A NullPointerException IF THERE IS NO + // CLASS + ReferenceType type = expr.field().declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + + Field e = null; + Field[] fields = jc.getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(expr.field().name()) + && fields[i].getType().equals( + expr.field().type())) { + e = fields[i]; + break; + } + } + if (e == null) + throw new NoSuchFieldException("field :" + expr.field() + + "not found"); + if (!e.isVolatile()) { + firstOrder = true; + } + // NEVER STARTED EDITING IT SO WE DON'T NEED TO RELEASE + // IT + // context.release(e.fieldInfo()); + + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's volatile. + firstOrder = false; + } catch (ClassNotFoundException cnfe) { + // A field wasn't found. Silently assume it's volatile. + firstOrder = false; + } + } + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + if (isLeaf(expr.expr())) { + firstOrder = true; + } + } + + public void visitNegExpr(NegExpr expr) { + if (isLeaf(expr.expr())) { + firstOrder = true; + } + } + + public void visitShiftExpr(ShiftExpr expr) { + if (isLeaf(expr.expr()) && isLeaf(expr.bits())) { + firstOrder = true; + } + } + + /** + * Once again, an expression that references a volatile static field is + * not first-order. + * + * @see #visitFieldExpr + */ + public void visitStaticFieldExpr(StaticFieldExpr expr) { + if (NO_ACCESS_PATHS) { + return; + } + try {// WILL CAUSE A NullPointerException IF THERE IS NO + // CLASS + ReferenceType type = expr.field().declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + + Field e = null; + Field[] fields = jc.getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(expr.field().name()) + && fields[i].getType().equals(expr.field().type())) { + e = fields[i]; + break; + } + } + if (e == null) + throw new NoSuchFieldException("field :" + expr.field() + + "not found"); + if (!e.isVolatile()) { + firstOrder = true; + } + // NEVER STARTED EDITING IT SO WE DON'T NEED TO RELEASE IT + // context.release(e.fieldInfo()); + + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's volatile. + firstOrder = false; + } catch (ClassNotFoundException cnfe) { + // A field wasn't found. Silently assume it's volatile. + firstOrder = false; + } + } + } + + /** + * Wrapper classes that are used to simulate pass-by-reference. That is, + * their values are changed inside methods. When used as parameters they + * must be declared as being final. + */ + class Int { + int value = 0; + } + + class Bool { + boolean value = false; + } + + int next = 0; + + /** + * Def represents a point at which a variable is defined. Each definition + * has a version number associated with it. + */ + abstract class Def { + int version = next++; + } + + /** + * RealDef represents a real occurrence of an expression. + */ + class RealDef extends Def { + Expr expr; + + public RealDef(Expr expr) { + this.expr = expr; + } + + public String toString() { + return "[" + expr + "]_" + version; + } + } + + /** + * Phi represents a PHI-statement (PHI-function) for merging an expression + * along two paths. + *

      + * Information about the operands to PHI-statements is maintained in the PHI + * class. + *

      + * A PHI-statement has the form: h = PHI(h, h) + * + * @see #operands + */ + class Phi extends Def { + Block block; // Block in which the PHI-statement occurs + + // Note that arrays are indexed by a block's preorder number. + + Def[] operands; // Operand to the PHI-statement + + boolean[] hasRealUse; // Is the ith operand a real use? + + boolean[] saveOperand; + + boolean downSafe; // downsafe flag (ds) + + boolean canBeAvail; // can_be_available (cba) + + boolean later; // later flag (later) + + boolean live; + + List leaves; + + /** + * Constructor. + * + * @param exprInfo + * The expression that this PHI-statement is associated with. + * @param block + * The block in which this PHI-statement occurs. Note that an + * PHI-statement can only occur in one block. + */ + public Phi(ExprInfo exprInfo, Block block) { + this.block = block; + + int size = cfg.size(); + + operands = new Def[size]; + hasRealUse = new boolean[size]; + saveOperand = new boolean[size]; + + leaves = null; + + downSafe = true; // Initially, ds = 1 + canBeAvail = true; // Initially, cba = 1 + later = true; // Initially, later = cba + live = false; // Initially, live = 0 + } + + /** + * Returns the block in which this PHI-statement is occurs. + */ + public Block block() { + return block; + } + + /** + * Sets the operands to a real occurrence of the expression. Leaves only + * apply to PHI-statements that are associated with a real occurrence of + * the expression. + * + * @see #rename + */ + public void setLeaves(List leaves) { + if (DEBUG) { + System.out.println("setting leaves of " + this + " to " + + leaves); + } + + this.leaves = new ArrayList(leaves); + } + + /** + * Returns the operands to the real occurrence represented by this + * PHI-statement. It is assumed that this PHI-statement represents a + * real occurrence. + */ + public List leaves() { + Assert.isTrue(leaves != null); + + Iterator iter = leaves.iterator(); + + while (iter.hasNext()) { + Expr e = (Expr) iter.next(); + Assert.isTrue( + e instanceof VarExpr || e instanceof ConstantExpr, + "not a leaf: " + e); + } + + return leaves; + } + + /** + * Returns the operands of the PHI-statement. This is just a list of all + * of the block's predacessors. + */ + public Collection operands() { + LinkedList v = new LinkedList(); + + Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + v.addFirst(operandAt(pred)); + } + + return v; + } + + /** + * Sets an operand of this PHI-statement. Recall that PHI-operands are + * associated with a predacessor block of the block in which the + * PHI-statement resides. + * + * @param block + * The block associated with the operand. + * @param def + * The PHI-definition that is the operand. + */ + public void setOperandAt(Block block, Def def) { + int blockIndex = cfg.preOrderIndex(block); + operands[blockIndex] = def; + + if (DEBUG) { + System.out.println(this); + } + } + + /** + * Returns the PHI-operand of this PHI-statement associated with a given + * block. Recall that PHI-operands are associated with a predacessor + * block of the block in which the PHI-statement resides. + */ + public Def operandAt(Block block) { + int blockIndex = cfg.preOrderIndex(block); + return operands[blockIndex]; + } + + /** + * Sets the "has real use" flag. + */ + public void setHasRealUse(Block block, boolean flag) { + int blockIndex = cfg.preOrderIndex(block); + + hasRealUse[blockIndex] = flag; + + if (DEBUG) { + System.out.println(this); + } + } + + public boolean hasRealUse(Block block) { + int blockIndex = cfg.preOrderIndex(block); + return hasRealUse[blockIndex]; + } + + /** + * + */ + public void setSaveOperand(Block block, boolean flag) { + int blockIndex = cfg.preOrderIndex(block); + saveOperand[blockIndex] = flag; + + if (DEBUG) { + System.out.println(this); + } + } + + public boolean saveOperand(Block block) { + int blockIndex = cfg.preOrderIndex(block); + return saveOperand[blockIndex]; + } + + /** + * Determines whether or not a PHI-operand satisfies "insert". For + * insert to hold, the following conditions must be met: 1. The + * PHI-statement is "will be available" 2. The PHI-operand is tack + * (null), or "has real use" is false and the operand is defined by an + * PHI-statement that does not satisfy "will be available". + * + * @param block + * The block with which a desired operand is associated with. + * Recall that PHI-operands are associated with the block + * that is a predacessor of the block in which they are + * contained. + */ + public boolean canInsert(Block block) { + int blockIndex = cfg.preOrderIndex(block); + + Def def = operands[blockIndex]; + + if (def == null) { + return true; + } + + if (!hasRealUse[blockIndex]) { + if (def instanceof Phi) { + Phi phi = (Phi) def; + + if (!phi.willBeAvail()) { + return true; + } + } + } + + return false; + } + + /** + * Returns whether or not an PHI-statement satisfies "will be + * available". "Will be available" is used to determine the locations in + * which to insert evaluations of the expression in the finalize() pass. + *

      + * willBeAvail = canBeAvail && !later + * + * @see #finalize + */ + public boolean willBeAvail() { + // WBA => CBA => DS + return canBeAvail && !later; + } + + /** + * Sets the "can be available" flag. + */ + public void setCanBeAvail(boolean flag) { + canBeAvail = flag; + + if (DEBUG) { + System.out.println(this); + } + } + + /** + * Returns the "can be available" flag. + */ + public boolean canBeAvail() { + return canBeAvail; + } + + /** + * Sets the "later" flag. If the later flag is false, it means that an + * evaluation of the expression may not be placed at any point below + * this PHI-statement without introducing a useless computation along + * some path. + */ + public void setLater(boolean flag) { + later = flag; + + if (DEBUG) { + System.out.println(this); + } + } + + /** + * Returns the "later" flag. + * + * @see #setLater + */ + public boolean later() { + return later; + } + + public void setLive(boolean flag) { + live = flag; + } + + public boolean live() { + return live; + } + + /** + * Sets the "down-safe" flag. An PHI-statement is "down-safe" if there + * is no path from the PHI-statement to the exit block that does not + * recalculate the expression. If an PHI-statement is "down-safe" it is + * worthwhile to attempt to hoist it up higher in the program. + *

      + * An PHI-statement is not "down-safe" when a) There is a path to exit + * along which the PHI-statement result is never used. b) There is a + * path to exit along which the only used of the result of the + * PHI-statement is an operand of an PHI-statement which itself is not + * "down-safe". + */ + public void setDownSafe(boolean flag) { + downSafe = flag; + + if (DEBUG) { + System.out.println(this); + } + } + + /** + * Returns whether or not this PHI-statement is "down-safe". + * + * @see #setDownSafe + */ + public boolean downSafe() { + return downSafe; + } + + /** + * Returns a textual representation of this PHI-statement. + */ + public String toString() { + String s = "Phi_" + version + "["; + + if (!downSafe) { + s += "!"; + } + + s += "DS,"; + + if (!canBeAvail) { + s += "!"; + } + + s += "CBA,"; + + if (!later) { + s += "!"; + } + + s += "later]("; + + if (operands != null) { + Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + int predIndex = cfg.preOrderIndex(pred); + + s += pred.label() + "="; + + Def operand = operands[predIndex]; + + if (operand == null) { + s += "undef["; + } else { + s += operand.version + "["; + } + + if (!hasRealUse[predIndex]) { + s += "!"; + } + + s += "HRU,"; + + if (!saveOperand[predIndex]) { + s += "!"; + } + + s += "save,"; + + if (!canInsert(pred)) { + s += "!"; + } + + s += "insert]"; + + if (e.hasNext()) { + s += ", "; + } + } + } + + s += ")"; + + return s; + } + } + + /** + * ExprInfo represents an expression that we are performing SSA-based PRE + * on. An occurrence of an expression can take one of three forms: 1) A real + * occurrence of an expression (h = a+b) 2) A (target of a) PHI function (h = + * PHI(...)) 3) An operand to a PHI function (PHI(h, ...)) + * + * The occurrences of an expression are ordered according to a preorder + * traversal of the CFG. + * + */ + private final class ExprInfo { + ExprKey key; // A unique key for an Expr instance + + private int numUses; // Number of uses (not defs) of this expr + + private List[] reals; // The real occurrences of this expression + + private boolean[] realsSorted; // Are the reals at a given block + + // sorted? + + private Phi[] phis; // PHI expressions for this occurrences + + Map defs; // Maps an Expr to its defining occurrence + + // "h" in the CFG. + Map availDefs; // + + Map saves; + + Map reloads; + + private Expr prototype; // The actual expression being represented + + private boolean isFinal; // Does the expression access a final + + // field? + + private boolean hasSideEffects; + + private boolean hasStackVariable; + + /** + * Constructor. + * + * @param expr + * The expression (real occurrence) represented by this + * ExprInfo. + * @param key + * A unique key by which this expression can be identified. + */ + public ExprInfo(Expr expr, ExprKey key) { + this.key = key; + + prototype = (Expr) expr.clone(); + + // Clean up the expression's children (remember that expr's + // children + // are also cloned, so we aren't changing the tree). + prototype.visitChildren(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + expr.target().setDef(null); + expr.target().setParent(null); + expr.replaceWith(expr.target(), false); + expr.cleanupOnly(); + expr.expr().cleanup(); + } + + public void visitVarExpr(VarExpr expr) { + expr.setDef(null); + } + + public void visitConstantExpr(ConstantExpr expr) { + } + + // The prototype expression should only + // contain StoreExpr, VarExpr, or + // ConstantExpr... + public void visitExpr(Expr expr) { + throw new RuntimeException(); + } + }); + + numUses = 0; + + reals = new ArrayList[cfg.size()]; + realsSorted = new boolean[cfg.size()]; + + for (int i = 0; i < reals.length; i++) { + reals[i] = new ArrayList(); + realsSorted[i] = false; + } + + phis = new Phi[cfg.size()]; + + defs = new LinkedHashMap(); + availDefs = new LinkedHashMap(); + saves = new LinkedHashMap(); + reloads = new LinkedHashMap(); + + if (prototype instanceof MemRefExpr) { + // Traverse the tree and determine whether expr accesses a final + // field. + FinalChecker fch = new FinalChecker(); + prototype.visit(fch); + isFinal = fch.isFinal; + + } else { + isFinal = true; + } + + // For PRE, RCs, UCs, stores, and possible reassignment + // through aliases are not considered side effects. + sideEffects.reset(); + prototype.visit(sideEffects); + + int flag = sideEffects.sideEffects(); + flag &= ~SideEffectChecker.STORE; + flag &= ~SideEffectChecker.ALIAS; + flag &= ~SideEffectChecker.RC; + flag &= ~SideEffectChecker.UC; + hasSideEffects = flag != 0; + + // Special case: allow RC(S) and UC(S). + if ((flag & SideEffectChecker.STACK) != 0) { + Assert.isTrue(prototype instanceof CheckExpr); + hasStackVariable = true; + } + } + + public boolean hasStackVariable() { + return hasStackVariable; + } + + public boolean hasSideEffects() { + return hasSideEffects; + } + + public int numUses() { + return numUses; + } + + public void cleanup() { + reals = null; + phis = null; + saves = null; + reloads = null; + defs = null; + availDefs = null; + prototype = null; + } + + // Reload is used in finalize + public void setReload(Expr expr, boolean flag) { + if (DEBUG) { + System.out.println(" setting reload for " + expr + + " to " + flag); + } + + reloads.put(expr, new Boolean(flag)); + } + + public boolean reload(Expr expr) { + Boolean flag = (Boolean) reloads.get(expr); + return flag != null && flag.booleanValue(); + } + + // Save is used in finalize + public void setSave(Expr expr, boolean flag) { + if (DEBUG) { + System.out.println(" setting save for " + expr + " to " + + flag); + } + + saves.put(expr, new Boolean(flag)); + } + + public boolean save(Expr expr) { + Boolean flag = (Boolean) saves.get(expr); + return flag != null && flag.booleanValue(); + } + + // AvailDef is used in finalize + public void setAvailDef(Def def, Def availDef) { + if (DEBUG) { + System.out.println(" setting avail def for " + def + + " to " + availDef); + } + + availDefs.put(def, availDef); + } + + public Def availDef(Def def) { + Def availDef = (Def) availDefs.get(def); + + if (DEBUG) { + System.out.println(" avail def for " + def + " is " + + availDef); + } + + return availDef; + } + + /** + * Sets the defining occurrence (the "h") of a given real occurrence. + */ + public void setDef(Expr expr, Def def) { + if (DEBUG) { + System.out.println(" setting def for " + expr + " to " + + def); + } + + if (def != null) { + defs.put(expr, def); + } else { + defs.remove(expr); + } + } + + /** + * Returns the Def (either a ReafDef or Phi) defining a given occurrence + * of the expression modeled by this ExprInfo. + */ + public Def def(Expr expr) { + Def def = (Def) defs.get(expr); + + if (DEBUG) { + System.out.println(" def for " + expr + " is " + def); + } + + return def; + } + + public Expr prototype() { + return prototype; + } + + /** + * Notifies this ExprInfo of the existence of another real occurrence of + * the expression. + */ + public void addReal(Expr real) { + if (!real.isDef()) { + numUses++; + } + + int blockIndex = cfg.preOrderIndex(real.block()); + reals[blockIndex].add(real); + realsSorted[blockIndex] = false; + } + + /** + * Notifies this ExprInfo of the existence of an PHI-statement for this + * expression. If the PHI is not already present, a new Phi instance is + * created for it and placed in the phis array. + * + * @param block + * The block at which the PHI occurs. + */ + public void addPhi(final Block block) { + int blockIndex = cfg.preOrderIndex(block); + + if (phis[blockIndex] == null) { + if (DEBUG) { + System.out.println(" add phi for " + prototype + " at " + + block); + } + + phis[blockIndex] = new Phi(this, block); + } + } + + /** + * Removes a PHI occurrence from the phis array. + */ + public void removePhi(Block block) { + int blockIndex = cfg.preOrderIndex(block); + phis[blockIndex] = null; + } + + /** + * Returns the PHI occurrence for this expression at a given Block in + * the code. + */ + public Phi exprPhiAtBlock(Block block) { + int blockIndex = cfg.preOrderIndex(block); + return phis[blockIndex]; + } + + /** + * Returns the real occurrences of this expression at a given Block in + * the code. + */ + public List realsAtBlock(Block block) { + int blockIndex = cfg.preOrderIndex(block); + + List r = reals[blockIndex]; + + if (!realsSorted[blockIndex]) { + sortExprs(r); + realsSorted[blockIndex] = true; + } + + return r; + } + + /** + * Returns a List of the real occurrences of the expression and any Kill + * expressions contained in a given Block in the code. + */ + public List occurrencesAtBlock(Block block) { + if (isFinal && !hasSideEffects) { + return realsAtBlock(block); + } + + int blockIndex = cfg.preOrderIndex(block); + + final List a = kills[blockIndex]; + final List r = reals[blockIndex]; + + if (!killsSorted[blockIndex]) { + sortKills(a); + killsSorted[blockIndex] = true; + } + + if (!realsSorted[blockIndex]) { + sortExprs(r); + realsSorted[blockIndex] = true; + } + + // return a list that is essentially a combination of the + // real occurrences and the kill expressions + return new AbstractList() { + public int size() { + return r.size() + a.size(); + } + + public boolean contains(Object obj) { + if (obj instanceof Kill) { + return a.contains(obj); + } else if (obj instanceof Expr) { + return r.contains(obj); + } + + return false; + } + + public Object get(int index) { + throw new UnsupportedOperationException(); + } + + public Iterator iterator() { + return new Iterator() { + Iterator aiter; + + Iterator riter; + + Kill anext; + + Expr rnext; + + { + aiter = a.iterator(); + riter = r.iterator(); + + if (aiter.hasNext()) { + anext = (Kill) aiter.next(); + } else { + anext = null; + } + + if (riter.hasNext()) { + rnext = (Expr) riter.next(); + } else { + rnext = null; + } + } + + public boolean hasNext() { + return anext != null || rnext != null; + } + + public Object next() { + boolean real = false; + + if (anext == null) { + if (rnext == null) { + throw new NoSuchElementException(); + } + + real = true; + + } else if (rnext == null) { + real = false; + + } else { + // Kills go first if keys are equal. + if (anext.key() <= rnext.key()) { + real = false; + } else { + real = true; + } + } + + if (real) { + Object t = rnext; + + if (riter.hasNext()) { + rnext = (Expr) riter.next(); + } else { + rnext = null; + } + + return t; + + } else { + Object t = anext; + + if (aiter.hasNext()) { + anext = (Kill) aiter.next(); + } else { + anext = null; + } + + return t; + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public ListIterator listIterator() { + throw new UnsupportedOperationException(); + } + }; + } // end occurrencesAtBlock() + + /** + * Sort a list of expressions into preorder. + * + * Recall that the key of each occurrence node was set to its preorder + * number in collectOccurrences. + */ + private void sortExprs(List list) { + Collections.sort(list, new Comparator() { + public int compare(Object a, Object b) { + if (a == b) { + return 0; + } + + int ka = ((Expr) a).key(); + int kb = ((Expr) b).key(); + + return ka - kb; + } + }); + } + + /** + * Sorts a lists of Kills into preorder. That is, the Kills in a given + * block are sorted by the pre-order number. + */ + private void sortKills(List list) { + Collections.sort(list, new Comparator() { + public int compare(Object a, Object b) { + if (a == b) { + return 0; + } + + int ka = ((Kill) a).key(); + int kb = ((Kill) b).key(); + + return ka - kb; + } + }); + } + + /** + * Print a textual description of this ExprInfo. + */ + protected void print() { + System.out.println("Print for " + prototype + "------------------"); + + cfg.visit(new PrintVisitor() { + Phi phi = null; + + public void visitBlock(Block block) { + phi = exprPhiAtBlock(block); + super.visitBlock(block); + } + + public void visitLabelStmt(LabelStmt stmt) { + super.visitLabelStmt(stmt); + + if (FlowGraph.label(stmt.label())) { + if (phi != null) { + println(phi); + phi = null; + } + } + } + }); + + System.out.println("End Print ----------------------------"); + } + } // end class ExprInfo + + /** + * Traverses a tree and determines if a final (class or instance) field is + * accessed. + */ + class FinalChecker extends TreeVisitor { + public boolean isFinal = true; + + public void visitExpr(Expr expr) { + if (isFinal) { + expr.visitChildren(this); + } + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + isFinal = false; + } + + public void visitFieldExpr(FieldExpr expr) { + try {// WILL CAUSE A NullPointerException IF THERE IS NO + // CLASS + ReferenceType type = expr.field().declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + + Field e = null; + Field[] fields = jc.getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(expr.field().name()) + && fields[i].getType().equals(expr.field().type())) { + e = fields[i]; + break; + } + } + + if (e == null) + throw new NoSuchFieldException("field :" + expr.field() + + "not found"); + if (!e.isFinal()) { + isFinal = false; + } + // WE NEVER STARTED EDITING IT SO DON'T HAVE TO RELEASE IT + // context.release(e.fieldInfo()); + + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's not final. + isFinal = false; + } catch (ClassNotFoundException cnfe) { + // A field wasn't found. Silently assume it's not final. + isFinal = false; + } + + if (isFinal) { + expr.visitChildren(this); + } + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + try {// WILL CAUSE A NullPointerException IF THERE IS NO + // CLASS + ReferenceType type = expr.field().declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + + Field e = null; + Field[] fields = jc.getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(expr.field().name()) + && fields[i].getType().equals(expr.field().type())) { + e = fields[i]; + break; + } + } + + if (e == null) + throw new NoSuchFieldException("field :" + expr.field() + + "not found"); + if (!e.isFinal()) { + isFinal = false; + } + // WE NEVER STARTED EDITING IT SO DON'T HAVE TO RELEASE IT + // context.release(e.fieldInfo()); + + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's not final. + isFinal = false; + } catch (ClassNotFoundException cnfe) { + // A field wasn't found. Silently assume it's not final. + isFinal = false; + } + + if (isFinal) { + expr.visitChildren(this); + } + } + } + + /** + * ExprWorklist is a worklist of expressions (represented by ExprInfo) + * containing all of the first-order expressions in the CFG (method). The + * worklist is assembled in collectOccurrences() and its expressions are + * used throughout SSAPRE. + * + * @see #collectOccurrences + */ + class ExprWorklist { + Map exprInfos; // A mapping between ExprKey and ExprInfo + + LinkedList exprs; // All the ExprInfos we know about + + public ExprWorklist() { + exprInfos = new LinkedHashMap(); + exprs = new LinkedList(); + } + + public boolean isEmpty() { + return exprs.isEmpty(); + } + + public ExprInfo removeFirst() { + ExprInfo exprInfo = (ExprInfo) exprs.removeFirst(); + exprInfos.remove(exprInfo.key); + return exprInfo; + } + + /** + * Add a real occurrence of an expression to the worklist. If necessary, + * an ExprInfo is created to represent the expression. + */ + public void addReal(Expr real) { + if (DEBUG) { + System.out.println(" add to worklist=" + real); + } + + ExprKey key = new ExprKey(real); + + ExprInfo exprInfo = (ExprInfo) exprInfos.get(key); + + if (exprInfo == null) { + exprInfo = new ExprInfo(real, key); + exprs.add(exprInfo); + exprInfos.put(key, exprInfo); + + if (DEBUG) { + System.out.println(" add info"); + } + } + + exprInfo.addReal(real); + } + + /** + * Adds a Kill expression to the worklist at a given block. + */ + public void addKill(Block block, Kill kill) { + if (DEBUG) { + System.out.println(" add alias to worklist=" + kill.expr + + " " + kill + " block: " + block.label()); + } + + int blockIndex = cfg.preOrderIndex(block); + kills[blockIndex].add(kill); + killsSorted[blockIndex] = false; + } + } + + /** + * Kill represents a point at which code cannot be hoisted across. + */ + abstract class Kill { + int key; + + Expr expr; + + /** + * Constructor. + * + * @param expr + * The expression that causes this kill. + */ + public Kill(Expr expr, int key) { + this.expr = expr; + this.key = key; + } + + public Kill(int key) { + this(null, key); + } + + public int key() { + return key; + } + + public String toString() { + return expr + ", " + key; + } + } + + /** + * ExceptionKill is a Kill that occurrs because an exception may be + * encountered. An ExceptionKill is used when a Block that begins a + * protected region or an expression that catches an exception is + * encountered. + * + * @see #collectOccurrences + */ + class ExceptionKill extends Kill { + public ExceptionKill(Expr expr, int key) { + super(expr, key); + } + + public ExceptionKill(int key) { + super(key); + } + } + + /** + * MemRefKill is a Kill that occurrs because a reference to a memory + * location may be made. A MemRefKill is used when a synchronized + * (monitorenter and monitorexit) block of code, or an expression that + * accesses a memory location (MemRefExpr) and defines a variable, or an + * expression that invokes a method is encountered. + * + * @see #collectOccurrences + */ + class MemRefKill extends Kill { + public MemRefKill(Expr expr, int key) { + super(expr, key); + } + + public MemRefKill(int key) { + super(key); + } + } + + /** + * Represents an expression and a hash code for that expression. + */ + class ExprKey { + Expr expr; + + int hash; + + public ExprKey(Expr expr) { + this.expr = expr; + this.hash = NodeComparator.hashCode(expr) + expr.type().hashCode(); + } + + public int hashCode() { + return hash; + } + + private List listChildren(Expr expr) { + final List children = new ArrayList(); + + if (expr instanceof StoreExpr) { + expr = ((StoreExpr) expr).target(); + } + + expr.visitChildren(new TreeVisitor() { + public void visitStoreExpr(StoreExpr expr) { + // Ignore the RHS. + children.add(expr.target()); + } + + public void visitExpr(Expr expr) { + children.add(expr); + } + }); + + return children; + } + + public boolean equals(Object obj) { + if (obj instanceof ExprKey) { + ExprKey other = (ExprKey) obj; + + if (!expr.type().equals(other.expr.type())) { + return false; + } + + if (!NodeComparator.equals(expr, other.expr)) { + return false; + } + + List children = listChildren(expr); + List otherChildren = listChildren(other.expr); + + if (children.size() != otherChildren.size()) { + return false; + } + + Iterator iter1 = children.iterator(); + Iterator iter2 = otherChildren.iterator(); + + while (iter1.hasNext() && iter2.hasNext()) { + Expr child1 = (Expr) iter1.next(); + Expr child2 = (Expr) iter2.next(); + + if (child1 instanceof StackExpr != child2 instanceof StackExpr) { + return false; + } + + if (child1 instanceof VarExpr && child2 instanceof VarExpr) { + + if (phiRelatedFind(child1.def()) != phiRelatedFind(child2 + .def())) { + + return false; + } + + } else { + Assert.isTrue(child1 instanceof ConstantExpr + || child2 instanceof ConstantExpr, "neither " + + child1 + " nor " + child2 + " are constants"); + + // If one is a constant the other must have the same + // value as the constant. + if (child1.valueNumber() != child2.valueNumber()) { + return false; + } + } + } + + if (iter1.hasNext() || iter2.hasNext()) { + // Size mismatch. + return false; + } + + return true; + } + + return false; + } + } // end class ExprKey + + /** + * + */ + Expr phiRelatedFind(Expr a) { + ArrayList stack = new ArrayList(); + + while (a != null) { + Object p = phiRelated.get(a); + + if (p == a || p == null) { + // Path compression. + Iterator iter = stack.iterator(); + + while (iter.hasNext()) { + p = iter.next(); + + if (p != a) { + phiRelated.put(p, a); + } + } + + return a; + } + + stack.add(a); + a = (Expr) p; + } + + return null; + } + + /** + * phiRelatedUnion associates a variable (local or stack) + */ + void phiRelatedUnion(Expr a, Expr b) { + Expr p = phiRelatedFind(a); + Expr q = phiRelatedFind(b); + if (p != q) { + phiRelated.put(p, q); + } + } + + public String traceMessage(String dateString) { + return " SSAPRE: " + dateString; + } + + public String preDebugMessage() { + return "-------------Before SSAPRE-----------"; + } + + public String postDebugMessage() { + return "-------------After SSAPRE------------"; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/SideEffectChecker.java b/src/edu/purdue/cs/bloat/trans/SideEffectChecker.java new file mode 100644 index 0000000..31240ba --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/SideEffectChecker.java @@ -0,0 +1,359 @@ +/* + * Class: SideEffectChecker + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.classfile.*; +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.ArrayLengthExpr; +import edu.purdue.cs.bloat.tree.ArrayRefExpr; +import edu.purdue.cs.bloat.tree.CallMethodExpr; +import edu.purdue.cs.bloat.tree.CallStaticExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CatchExpr; +import edu.purdue.cs.bloat.tree.FieldExpr; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.MonitorStmt; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.RCExpr; +import edu.purdue.cs.bloat.tree.StackExpr; +import edu.purdue.cs.bloat.tree.StackManipStmt; +import edu.purdue.cs.bloat.tree.StaticFieldExpr; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.UCExpr; +import edu.purdue.cs.bloat.tree.ZeroCheckExpr; + +/** + * SideEffectChecker traverses a tree and determines if a node has + * any side effects such as changing the stack, calling a function, or + * performing a residency check. The side effects are represented by an integer + * whose bits represent a certain kind of side effect. + * + *

      + * + * SideEffectChecker is a TreeVisitor. The way it works + * is that after a SideEffectChecker is reset, an expression tree + * Node is visited to determine whether or not it has side effects. + * Neat. + */ +public class SideEffectChecker extends TreeVisitor { + private int sideEffects = 0; + + public static final int STACK = (1 << 0); + + public static final int THROW = (1 << 1); + + public static final int CALL = (1 << 2); + + public static final int SYNC = (1 << 3); + + public static final int ALLOC = (1 << 4); // Allocates memory + + public static final int RC = (1 << 5); + + public static final int UC = (1 << 6); + + public static final int STORE = (1 << 7); + + public static final int ALIAS = (1 << 8); + + public static final int VOLATILE = (1 << 9); + + private EditorContext context; + + /** + * Constructor. The Context is needed to determine whether or not + * a field is VOLATILE, etc. + */ + public SideEffectChecker(EditorContext context) { + this.context = context; + } + + public int sideEffects() { + return sideEffects; + } + + public boolean hasSideEffects() { + return sideEffects != 0; + } + + public void reset() { + sideEffects = 0; + } + + public void visitStoreExpr(StoreExpr expr) { + sideEffects |= STORE; + expr.visitChildren(this); + } + + public void visitLocalExpr(LocalExpr expr) { + if (expr.isDef()) { + sideEffects |= STORE; + } + expr.visitChildren(this); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + sideEffects |= THROW; + expr.visitChildren(this); + } + + public void visitRCExpr(RCExpr expr) { + sideEffects |= RC; + expr.visitChildren(this); + } + + public void visitUCExpr(UCExpr expr) { + sideEffects |= UC; + expr.visitChildren(this); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + sideEffects |= THROW | ALLOC; + expr.visitChildren(this); + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + sideEffects |= THROW | ALLOC; + expr.visitChildren(this); + } + + public void visitCatchExpr(CatchExpr expr) { + // Stack change + sideEffects |= STACK; + expr.visitChildren(this); + } + + public void visitNewExpr(NewExpr expr) { + // Memory allocation + sideEffects |= ALLOC; + expr.visitChildren(this); + } + + public void visitStackExpr(StackExpr expr) { + // Stack change + sideEffects |= STACK; + + if (expr.isDef()) { + sideEffects |= STORE; + } + + expr.visitChildren(this); + } + + public void visitCastExpr(CastExpr expr) { + // ClassCastException + if (expr.castType() instanceof ReferenceType) { + sideEffects |= THROW; + } + expr.visitChildren(this); + } + + public void visitArithExpr(ArithExpr expr) { + // DivideByZeroException -- handled by ZeroCheckExpr + /* + * if (expr.operation() == ArithExpr.DIV || expr.operation() == + * ArithExpr.REM) { + * + * if (expr.type().isIntegral() || expr.type().equals(Type.LONG)) { + * sideEffects |= THROW; } } + */ + + expr.visitChildren(this); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + // NullPointerException + sideEffects |= THROW; + expr.visitChildren(this); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + // NullPointerException, ArrayIndexOutOfBoundsException, + // ArrayStoreException + sideEffects |= THROW; + + if (expr.isDef()) { + sideEffects |= STORE; + } + + sideEffects |= ALIAS; + + expr.visitChildren(this); + } + + public void visitFieldExpr(FieldExpr expr) { + // NullPointerException -- handled by ZeroCheckExpr + /* + * sideEffects |= THROW; + */ + + if (expr.isDef()) { + sideEffects |= STORE; + } + + MemberRef field = expr.field(); + try { + try { + ReferenceType type = field.declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + Field[] fields = jc.getFields(); + Field e = null; + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(field.name()) + && fields[i].getType().equals(field.type())) { + e = fields[i]; + break; + } + } + if (e == null) + throw new NoSuchFieldException("field :" + field + + "not found"); + + if (!e.isFinal()) { + sideEffects |= ALIAS; + } + + if (e.isVolatile()) { + sideEffects |= VOLATILE; + } + // WE NEVER STARTED EDITING IT SO WE DON'T NEED TO RELEASE IT + // context.release(e.fieldInfo()); + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's not final and + // is volatile. + sideEffects |= ALIAS; + sideEffects |= VOLATILE; + } + } catch (ClassNotFoundException cnfe) { + System.out.println(cnfe.getMessage()); + } + expr.visitChildren(this); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + if (expr.isDef()) { + sideEffects |= STORE; + } + + MemberRef field = expr.field(); + try { + try { + ReferenceType type = field.declaringClass(); + if (type instanceof ArrayType) {// TODO + Type elementType = ((ArrayType) type).getElementType(); + if (elementType instanceof BasicType) { + return; // Already loaded + } + type = (ObjectType) elementType; + } + JavaClass jc = context.loadClass(((ObjectType) type) + .getClassName()); + + Field e = null; + Field[] fields = jc.getFields(); + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(field.name()) + && fields[i].getType().equals(field.type())) { + e = fields[i]; + break; + } + } + if (e == null) + throw new NoSuchFieldException("field :" + field + + "not found"); + + if (e.isVolatile()) { + sideEffects |= VOLATILE; + } + // WE NEVER STARTED EDITING IT SO WE DON'T NEED TO RELEASE IT + // context.release(e.fieldInfo()); + } catch (NoSuchFieldException e) { + // A field wasn't found. Silently assume it's volatile. + sideEffects |= VOLATILE; + } + } catch (ClassNotFoundException cnfe) { + System.out.println(cnfe.getMessage()); + } + expr.visitChildren(this); + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + // Call + sideEffects |= THROW | CALL; + expr.visitChildren(this); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + // Call + sideEffects |= THROW | CALL; + expr.visitChildren(this); + } + + public void visitMonitorStmt(MonitorStmt stmt) { + // Synchronization + sideEffects |= THROW | SYNC; + stmt.visitChildren(this); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + // Stack change + sideEffects |= STACK; + stmt.visitChildren(this); + } +} diff --git a/src/edu/purdue/cs/bloat/trans/StackOpt.java b/src/edu/purdue/cs/bloat/trans/StackOpt.java new file mode 100644 index 0000000..c6df655 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/StackOpt.java @@ -0,0 +1,327 @@ +/* + * Class: StackOpt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.codegen.CodeGenerator; +import edu.purdue.cs.bloat.editor.InstructionAdapter; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.tree.Node; + +public class StackOpt extends InstructionAdapter implements Optimization { + + int stackHeight; + + int minStackHeight; + + static final boolean DEBUG = false; + + public void transform(MethodState state) { + // generate code without doing liveness or register allocation + CodeGenerator codegen = new CodeGenerator(state); + codegen.replacePhis().generateCode(); + + // do stack optimization on the bytecode + state.commitChanges(); + + transform(state.methodGen()); + + state.rebuildFlowGraph(); + } + + // StackOpt needs use and definition and label information to perform + // transformations. + // Consequently CodeGenerator must have been run to produce the code + // supplied to StackOpt.transform. + // Because of this I assume that labels and use and definition + // information are in place. + // This is currently implemented using attributes in InstructionHandles, + // the keys being "Label" and + // "Def". --Arrin + private void transform(MethodGen method) { + for (InstructionHandle i = method.getInstructionList().getEnd(); i != null; // && i + // != + // method.getInstructionList().getStart(); + i = i.getPrev()) { + + Instruction inst = i.getInstruction(); + boolean isWide; + + if (inst instanceof LoadInstruction) { + isWide = (((LoadInstruction) inst).getType( + method.getConstantPool()).getSize() == 2); + } else if (isArrayLoadInstruction(inst)) { + isWide = (((ArrayInstruction) inst).getType( + method.getConstantPool()).getSize() == 2); + } else { + continue; + } + + if (DEBUG) + System.out.println("Considering: " + i); + + stackHeight = 0; + + boolean seenLabel = FlowGraph.label(i); // If 'i' is a label we + // don't do anything. + for (InstructionHandle j = i.getPrev();; j = j.getPrev()) { + + // stop at the begining of the code or a basic block. + // As Labels are also instructions + if (j == null || seenLabel) + break; + + if (stackHeight == -1 + && ((hasSameDef(i, j) && isLoadInstruction(j)) || dupRun( + j, i))) { + if (forwardCountCheck(method, j, i, -1)) { + // found a type 0 relation with a load + if (DEBUG) { + System.out.println("load type 0: " + j + " " + i); + } + if (isWide) { + method.getInstructionList().append(j, new DUP2()); + } else { + method.getInstructionList().append(j, new DUP()); + // add dup + } + i.setInstruction(new NOP()); + // TODO: Why is deleting this a problem? + // I would have deleted it but it seems to cause all + // sorts of evil + } + break; // done, even if final check failed. + } else if (stackHeight == 0 && hasSameDef(i, j)) { + if (isStoreInstruction(j)) { + if (forwardCountCheck(method, j, i, 0)) { + // found a type 0 with a store + if (DEBUG) { + System.out.println("store type 0: " + j + " " + + i); + } + if (isWide) { + method.getInstructionList().append(j, + j.getInstruction()); + j.setInstruction(new DUP2()); + } else { + method.getInstructionList().append(j, + j.getInstruction()); + j.setInstruction(new DUP()); + + } + i.setInstruction(new NOP()); + // I would have deleted it but it seems to cause + // all sorts of evil + + } + break; + } else if (isLoadInstruction(j) && !isWide) { + // can't do type 1s with wides. + if (forwardCountCheck(method, j, i, -1)) { + // found a type 1 with a load + if (DEBUG) + System.out.println("load type 1: " + j + " " + + i); + method.getInstructionList().append(j, new DUP()); + System.out.println("HERE1 inserting SWAP"); + i.setInstruction(new SWAP()); + } + break; + } + } else if (stackHeight == 1 && hasSameDef(i, j)) { + if (isStoreInstruction(j) && !isWide) { // can't do type 1 + // with wides + + if (forwardCountCheck(method, j, i, 0)) { + // type 1 for stores + if (DEBUG) + System.out.println("store type 1: " + j + " " + + i); + method.getInstructionList().append(j, + j.getInstruction()); + j.setInstruction(new DUP()); + System.out.println("HERE2 inserting SWAP"); + i.setInstruction(new SWAP()); + } + break; + } + } + heightChange(method, j.getInstruction()); + + if (FlowGraph.label(j)) { + seenLabel = true; + } + } + } + + method.removeNOPs(); + method.getInstructionList().setPositions(); + if (DEBUG) { + System.out.println("Method:"); + System.out.println(method); + for (InstructionHandle ih = method.getInstructionList().getStart(); ih != null; ih = ih + .getNext()) { + if (FlowGraph.label(ih)) + System.out.print("(L)"); + else + System.out.print(" "); + System.out.print(" " + ih.getPosition() + ": " + + ih.getInstruction()); + if (ih.getAttribute("Def") != null) + System.out.print("(" + ih.getAttribute("Def") + ")"); + System.out.println(""); + } + // System.out.println(method.getMethod().getCode()); + System.out.println(); + System.out.println("LocalVariables: "); + LocalVariableGen[] locals = method.getLocalVariables(); + for (int i = 0; i < locals.length; i++) { + System.out.println(" " + locals[i]); + } + System.out.println("LocalVariable size: " + method.getMaxLocals()); + CodeExceptionGen[] excptns = method.getExceptionHandlers(); + if (excptns != null) { + System.out.println("ExceptionHandlers: "); + for (int j = 0; j < excptns.length; j++) + System.out.println(" " + excptns[j]); + } + } + + method.setMaxStack(); + method.setMaxLocals(); + method.update(); + } + + boolean forwardCountCheck(MethodGen m, InstructionHandle j, + InstructionHandle i, int bound) { + + stackHeight = 0; + minStackHeight = 0; + + for (InstructionHandle k = j.getNext(); k != i; k = k.getNext()) { + heightChange(m, k.getInstruction()); + if (minStackHeight < bound) + return false; + } + + return true; + } + + boolean dupRun(InstructionHandle j, InstructionHandle i) { + InstructionHandle k = j; + Instruction inst = k.getInstruction(); + + if (!(inst instanceof DUP)) + return false; + + do { + k = k.getPrev(); + inst = k.getInstruction(); + + if (isLoadInstruction(inst) && hasSameDef(i, k)) { + return true; + } + } while (!FlowGraph.label(k) && k.getInstruction() instanceof DUP); + + return (isLoadInstruction(inst)) && hasSameDef(i, k); + } + + void heightChange(MethodGen method, Instruction inst) { + stackHeight = stackHeight - inst.consumeStack(method.getConstantPool()); + + if (stackHeight < minStackHeight) + minStackHeight = stackHeight; + + stackHeight = stackHeight + inst.produceStack(method.getConstantPool()); + } + + private boolean hasSameDef(InstructionHandle i, InstructionHandle j) { + Node iDef = (Node) i.getAttribute("Def"); + Node jDef = (Node) j.getAttribute("Def"); + return ((iDef != null) && (jDef != null) && iDef == jDef); + } + + public String traceMessage(String dateString) { + return " New stack optimization: " + dateString; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + private static boolean isLoadInstruction(InstructionHandle handle) { + return isLoadInstruction(handle.getInstruction()); + } + + private static boolean isLoadInstruction(Instruction inst) { + return inst instanceof LoadInstruction || isArrayLoadInstruction(inst); + } + + private static boolean isArrayLoadInstruction(Instruction inst) { + return inst instanceof IALOAD || inst instanceof FALOAD + || inst instanceof AALOAD || inst instanceof BALOAD + || inst instanceof CALOAD || inst instanceof LALOAD + || inst instanceof DALOAD || inst instanceof SALOAD; + } + + private static boolean isStoreInstruction(InstructionHandle handle) { + return isStoreInstruction(handle.getInstruction()); + } + + private static boolean isStoreInstruction(Instruction inst) { + return inst instanceof StoreInstruction + || isArrayStoreInstruction(inst); + } + + private static boolean isArrayStoreInstruction(Instruction inst) { + return inst instanceof IASTORE || inst instanceof FASTORE + || inst instanceof AASTORE || inst instanceof BASTORE + || inst instanceof CASTORE || inst instanceof LASTORE + || inst instanceof DASTORE || inst instanceof SASTORE; + } + +} diff --git a/src/edu/purdue/cs/bloat/trans/StackOptimization.java b/src/edu/purdue/cs/bloat/trans/StackOptimization.java new file mode 100644 index 0000000..1134f12 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/StackOptimization.java @@ -0,0 +1,76 @@ +/* + * Class: StackOptimization + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import java.util.Iterator; +import java.util.List; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; + +/** + * This is the old stack optimizer. The actual optimization stuff is in + * tree/StackOptimizationData.java, this class just provides a convenient plugin + * interface to it. + * + * This is completely unrelated to StackOpt. + */ +public class StackOptimization implements Optimization { + public String traceMessage(String dateString) { + return " Old stack optimization: " + dateString; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } + + public void transform(MethodState state) { + List blocks = state.controlFlowGraph().preOrder(); + + for (Iterator i = blocks.iterator(); i.hasNext();) + ((Block) i.next()).stackOptimizer().optimize(); + + } +} diff --git a/src/edu/purdue/cs/bloat/trans/ValueFolder.java b/src/edu/purdue/cs/bloat/trans/ValueFolder.java new file mode 100644 index 0000000..d60e24a --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/ValueFolder.java @@ -0,0 +1,2014 @@ +/* + * Class: ValueFolder + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import org.apache.bcel.generic.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.tree.ArithExpr; +import edu.purdue.cs.bloat.tree.CastExpr; +import edu.purdue.cs.bloat.tree.CheckExpr; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.GotoStmt; +import edu.purdue.cs.bloat.tree.IfCmpStmt; +import edu.purdue.cs.bloat.tree.IfStmt; +import edu.purdue.cs.bloat.tree.IfZeroStmt; +import edu.purdue.cs.bloat.tree.InitStmt; +import edu.purdue.cs.bloat.tree.JumpStmt; +import edu.purdue.cs.bloat.tree.LocalExpr; +import edu.purdue.cs.bloat.tree.NegExpr; +import edu.purdue.cs.bloat.tree.NewArrayExpr; +import edu.purdue.cs.bloat.tree.NewExpr; +import edu.purdue.cs.bloat.tree.NewMultiArrayExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiJoinStmt; +import edu.purdue.cs.bloat.tree.RCExpr; +import edu.purdue.cs.bloat.tree.ReplaceVisitor; +import edu.purdue.cs.bloat.tree.ShiftExpr; +import edu.purdue.cs.bloat.tree.StoreExpr; +import edu.purdue.cs.bloat.tree.SwitchStmt; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.tree.UCExpr; +import edu.purdue.cs.bloat.tree.ZeroCheckExpr; +import edu.purdue.cs.bloat.util.Assert; +import edu.purdue.cs.bloat.util.ResizeableArrayList; + +import java.io.*; +import java.util.*; + +/** + * ValueFolder uses a TreeVisitor to examine a + * Node to determine whether or not it can be simplified. For + * instance, redundent checks are removed and algebraic identities are + * exploited. + * + * @see SideEffectChecker + */ +class ValueFolder extends TreeVisitor { + Node node; // (New) value of the folded node + + // If a value number has been reduced down to a constant number + // (ConstantExpr), this array maintains that mapping. + ResizeableArrayList values; + + // Keeps track of which value numbers correspond to expressions that + // allocate new objects (NewExpr, NewArrayExpr, and NewMultiArrayExpr). + BitSet news; + + // Local variable representing the this pointer + LocalExpr thisPtr; + + // Used to determine whether or not a Node in the expression tree + // has side effects + SideEffectChecker sideEffects; + + // Do we actually replace expressions with common value #'s? Or do + // we just calculate the value numbers. + boolean replace; + + // + ArrayList clean; + + /** + * Constructor. + * + * @param replace + * Do we replace values with their folded values? + * @param context + * Needed to create a SideEffectChecker + */ + public ValueFolder(boolean replace, EditorContext context) { + this.node = null; + this.replace = replace; + this.clean = new ArrayList(); + this.sideEffects = new SideEffectChecker(context); + + this.values = new ResizeableArrayList(); + this.news = new BitSet(); + this.thisPtr = null; + } + + /** + * Cleans up nodes that + */ + public void cleanup() { + Iterator iter = clean.iterator(); + + while (iter.hasNext()) { + Node node = (Node) iter.next(); + node.cleanup(); + } + } + + /** + * Returns the simplified version of the most recently simplified + * Node. Returns null if no simplification occurred. + */ + public Node replacement() { + return node; + } + + public void visitNode(Node node) { + } + + public void visitLocalExpr(LocalExpr expr) { + // Determines whether or not the LocalExpr in question is the this + // pointer. If the LocalExpr resides within an InitStmt, and the + // LocalExpr is the first variable defined by the InitStmt, it is + // the this pointer. + + if (thisPtr != null) { + return; + } + + if (expr.parent() instanceof InitStmt) { + InitStmt stmt = (InitStmt) expr.parent(); + + MethodGen method = stmt.block().graph().method(); + + if (!method.isStatic()) { + Assert.isTrue(stmt.targets().length > 0); + + if (expr == stmt.targets()[0]) { + thisPtr = expr; + + if (ValueFolding.DEBUG) { + System.out.println("this = " + thisPtr); + } + } + } + } + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + if (!(stmt.target() instanceof LocalExpr)) { + return; + } + + // A PhiJoinStmt may be eliminated if it is meaningless (all of + // its operands have the same value number) or it is redundent + // (its value is already computed by another PhiJoinStmt). + + int v = stmt.valueNumber(); + int ov = -1; // Operand's value number + + Iterator iter = stmt.operands().iterator(); + + // Examine each operand of the PhiJoinStmt. + while (iter.hasNext()) { + Expr expr = (Expr) iter.next(); + + if (replace) { + sideEffects.reset(); + expr.visit(sideEffects); + + if (sideEffects.hasSideEffects()) { + return; + } + } + + if (expr.valueNumber() == -1) { + continue; + } + + if (ov != -1 && expr.valueNumber() != ov) { + // At least two operands have different value numbers. The + // PhiJoinStmt cannot be simplified. + return; + } + + ov = expr.valueNumber(); + } + + if (ov == -1) { + // We cannot replace an PhiJoinStmt with no operands + Assert.isFalse(replace && stmt.operands().size() == 0); + ov = v; + } + + // All operands have same value number or -1. + values.ensureSize(Math.max(v, ov) + 1); + ConstantExpr value = (ConstantExpr) values.get(ov); + + if (value != null) { + node = value; + values.set(v, value); + + if (replace) { + stmt.block().tree().removeStmt(stmt); + } + } + } + + public void visitStoreExpr(StoreExpr expr) { + if (expr.expr() instanceof CheckExpr) { + // This makes copy propagation more effective after PRE. + // x := rc(y) --> rc(x := y) + CheckExpr rc = (CheckExpr) expr.expr(); + + if (replace) { + Node parent = expr.parent(); + + // x := rc(y) --> x := y + expr.visit(new ReplaceVisitor(rc, rc.expr())); + + // rc(y) --> rc(x := y) + rc.visit(new ReplaceVisitor(rc.expr(), expr)); + + // x := rc(y) --> rc(x := y) + parent.visit(new ReplaceVisitor(expr, rc)); + + node = rc; + + } else { + // Don't bother. + node = null; + } + + return; + } + + if (expr.target() instanceof LocalExpr) { + // If we're assigning into a local variable, + + int v = expr.valueNumber(); + int lv = expr.target().valueNumber(); + int rv = expr.expr().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + expr.expr().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + } + + ConstantExpr rexpr = (ConstantExpr) values.get(rv); + + if (rexpr != null) { + // The entire StoreExpr has a constant value + if (!replace) { + node = rexpr; + values.set(v, node); + + } else if (!reffects && expr.target().uses().size() == 0) { + // Replace the rhs with constant mapped to its value + // number + node = new ConstantExpr(rexpr.value(), expr.type()); + node.setValueNumber(v); + values.set(v, node); + expr.replaceWith(node); + } + } + } + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + // Keep track of the value numbers of expressions that create new + // objects. + + if (expr.valueNumber() != -1) { + if (ValueFolding.DEBUG) { + System.out.println("New " + expr); + } + + news.set(expr.valueNumber()); + } + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + // Keep track of the value numbers of expressions that create new + // objects. + + if (expr.valueNumber() != -1) { + if (ValueFolding.DEBUG) { + System.out.println("New " + expr); + } + + news.set(expr.valueNumber()); + } + } + + public void visitNewExpr(NewExpr expr) { + // Keep track of the value number of expression that create new + // objects. + + if (expr.valueNumber() != -1) { + if (ValueFolding.DEBUG) { + System.out.println("New " + expr); + } + + news.set(expr.valueNumber()); + } + } + + public void visitRCExpr(RCExpr expr) { + boolean move = false; // Can we remove the RCExpr + + int v = expr.expr().valueNumber(); + + if (expr.expr() instanceof RCExpr) { + // If the expression being checked for residency is itself an + // RCExpr, then the outer one is redundent. + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding redundant rc in " + expr); + } + + } else if (v != -1) { + if (thisPtr != null && thisPtr.valueNumber() == v) { + // We know that the this pointer is always resident, so we + // don't need to perform an rc on it. + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding rc(this) = " + expr); + } + + } else if (news.get(v)) { + // We know that the result of a new expression is always + // resident, so we don't need to perform an rc on it. + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding rc(new) = " + expr); + } + } + } + + if (move) { + node = expr.expr(); + + if (replace) { + // rc(this) --> this + // rc(rc(x)) --> rc(x) + node.setParent(null); + expr.replaceWith(node, false); + expr.cleanupOnly(); + } + } + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + boolean move = false; + + int v = expr.expr().valueNumber(); + + if (expr.expr() instanceof ZeroCheckExpr) { + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding redundant ZeroCheck in " + expr); + } + + } else if (v != -1) { + if (thisPtr != null && thisPtr.valueNumber() == v) { + // The this pointer can't be null. + + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding ZeroCheck(this) = " + expr); + } + + } else if (news.get(v)) { + // Newly created objects can't be null. + move = true; + + if (ValueFolding.DEBUG) { + System.out.println("folding ZeroCheck(new) = " + expr); + } + + } else { + // Check to see if the value number associated with the + // expression being checked for zero-ness has a constant value + // of zero. + ConstantExpr eexpr = null; + + if (v < values.size()) { + eexpr = (ConstantExpr) values.get(v); + } + + if (eexpr != null) { + Object value = eexpr.value(); + + if (value instanceof Long) { + if (((Long) value).longValue() != 0L) { + move = true; + } + + } else if (value instanceof Byte || value instanceof Short + || value instanceof Integer) { + if (((Number) value).intValue() != 0) { + move = true; + } + + } else if (value instanceof Character) { + if (((Character) value).charValue() != 0) { + move = true; + } + } + } + } + } + + if (move) { + node = expr.expr(); + + if (replace) { + // ZeroCheck(1) --> 1 + // ZeroCheck(this) --> this + // ZeroCheck(ZeroCheck(x)) --> ZeroCheck(x) + node.setParent(null); + expr.replaceWith(node, false); + expr.cleanupOnly(); + } + } + } + + public void visitUCExpr(UCExpr expr) { + if (expr.expr() instanceof UCExpr) { + // Remove redundent update checks + + UCExpr uc = (UCExpr) expr.expr(); + + if (uc.kind() == expr.kind()) { + node = uc; + + if (replace) { + // uc(uc(x)) --> uc(x) + expr.visit(new ReplaceVisitor(uc, uc.expr())); + uc.cleanupOnly(); + } + } + } + } + + public void visitArithExpr(ArithExpr expr) { + if (Assert.isIntegral(expr.left().type())) { + foldArithInteger(expr); + } else if (expr.left().type().equals(Type.LONG)) { + foldArithLong(expr); + } else if (expr.left().type().equals(Type.FLOAT)) { + foldArithFloat(expr); + } else if (expr.left().type().equals(Type.DOUBLE)) { + foldArithDouble(expr); + } + } + + /** + * Look for integer arithmetic identities... + */ + private void foldArithInteger(ArithExpr expr) { + int v = expr.valueNumber(); + int lv = expr.left().valueNumber(); + int rv = expr.right().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + ConstantExpr lexpr = null; + ConstantExpr rexpr = null; + + if (0 <= lv && 0 <= lv && lv < values.size()) { + lexpr = (ConstantExpr) values.get(lv); + } + + if (0 <= rv && 0 <= rv && rv < values.size()) { + rexpr = (ConstantExpr) values.get(rv); + } + + boolean leffects = false; + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + expr.left().visit(sideEffects); + leffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + expr.right().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + } + + if (lexpr != null && rexpr != null && !leffects && !reffects) { + // Okay, both of the ArithExpr's operands evaluate to constants + // and there are no side effects. We may be able to exploit + // various algebraic identites. + Integer value = null; + + int lval = ((Number) lexpr.value()).intValue(); + int rval = ((Number) rexpr.value()).intValue(); + + switch (expr.operation()) { + case ArithExpr.ADD: + value = new Integer(lval + rval); + break; + case ArithExpr.AND: + value = new Integer(lval & rval); + break; + case ArithExpr.DIV: + if (rval != 0) { + value = new Integer(lval / rval); + } + break; + case ArithExpr.MUL: + value = new Integer(lval * rval); + break; + case ArithExpr.IOR: + value = new Integer(lval | rval); + break; + case ArithExpr.REM: + if (rval != 0) { + value = new Integer(lval % rval); + } + break; + case ArithExpr.SUB: + value = new Integer(lval - rval); + break; + case ArithExpr.XOR: + value = new Integer(lval ^ rval); + break; + default: + break; + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + + } else if (lexpr == null && rexpr != null && !reffects) { + // Only the right operand evaluates to a constant... + int rval = ((Number) rexpr.value()).intValue(); + + switch (rval) { + case 0: + // x + 0 = x + // x - 0 = x + // x | 0 = x + // x * 0 = 0 + // x & 0 = 0 + switch (expr.operation()) { + case ArithExpr.ADD: + case ArithExpr.SUB: + case ArithExpr.IOR: + node = expr.left(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.MUL: + case ArithExpr.AND: + node = new ConstantExpr(new Integer(0), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + break; + case 1: + // x * 1 = x + // x / 1 = x + // x % 1 = 0 + switch (expr.operation()) { + case ArithExpr.MUL: + case ArithExpr.DIV: + node = expr.left(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.REM: + node = new ConstantExpr(new Integer(0), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + break; + case -1: + // x * -1 = -x + // x / -1 = -x + switch (expr.operation()) { + case ArithExpr.MUL: + case ArithExpr.DIV: + if (replace) { + expr.left().setParent(null); + node = new NegExpr(expr.left(), expr.type()); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + + } else { + node = new NegExpr((Expr) expr.left().clone(), expr + .type()); + node.setValueNumber(v); + clean.add(node); + } + break; + } + break; + } + + } else if (lexpr != null && rexpr == null && !leffects) { + // Only left operand resolves to a constant value... + int lval = ((Number) lexpr.value()).intValue(); + + switch (lval) { + case 0: + // 0 + x = x + // 0 - x = -x + // 0 | x = x + // 0 * x = 0 + // 0 & x = 0 + switch (expr.operation()) { + case ArithExpr.ADD: + case ArithExpr.IOR: + node = expr.right(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.SUB: + if (replace) { + expr.right().setParent(null); + node = new NegExpr(expr.right(), expr.type()); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } else { + node = new NegExpr((Expr) expr.right().clone(), expr + .type()); + node.setValueNumber(v); + clean.add(node); + } + break; + case ArithExpr.MUL: + case ArithExpr.AND: + node = new ConstantExpr(new Integer(0), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + break; + case 1: + // 1 * x = x + switch (expr.operation()) { + case ArithExpr.MUL: + node = expr.right(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } + break; + } + break; + case -1: + // -1 * x = -x + switch (expr.operation()) { + case ArithExpr.MUL: + if (replace) { + expr.right().setParent(null); + node = new NegExpr(expr.right(), expr.type()); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } else { + node = new NegExpr((Expr) expr.right().clone(), expr + .type()); + node.setValueNumber(v); + clean.add(node); + } + break; + } + break; + } + } + } + + /** + * Look for long arithmetic indentities... + */ + private void foldArithLong(ArithExpr expr) { + int v = expr.valueNumber(); + int lv = expr.left().valueNumber(); + int rv = expr.right().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + ConstantExpr lexpr = null; + ConstantExpr rexpr = null; + + if (0 <= lv && lv < values.size()) { + lexpr = (ConstantExpr) values.get(lv); + } + + if (0 <= rv && rv < values.size()) { + rexpr = (ConstantExpr) values.get(rv); + } + + boolean leffects = false; + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + expr.left().visit(sideEffects); + leffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + expr.right().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + } + + if (lexpr != null && rexpr != null && !leffects && !reffects) { + Number value = null; + + long lval = ((Long) lexpr.value()).longValue(); + long rval = ((Long) rexpr.value()).longValue(); + + switch (expr.operation()) { + case ArithExpr.ADD: + value = new Long(lval + rval); + break; + case ArithExpr.AND: + value = new Long(lval & rval); + break; + case ArithExpr.DIV: + if (rval != 0) { + value = new Long(lval / rval); + } + break; + case ArithExpr.MUL: + value = new Long(lval * rval); + break; + case ArithExpr.IOR: + value = new Long(lval | rval); + break; + case ArithExpr.REM: + if (rval != 0L) { + value = new Long(lval % rval); + } + break; + case ArithExpr.SUB: + value = new Long(lval - rval); + break; + case ArithExpr.XOR: + value = new Long(lval ^ rval); + break; + case ArithExpr.CMP: + if (lval > rval) { + value = new Integer(1); + } else if (lval < rval) { + value = new Integer(-1); + } else { + value = new Integer(0); + } + break; + default: + break; + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + } else if (lexpr == null && rexpr != null) { + long rval = ((Long) rexpr.value()).longValue(); + + if (reffects) { + return; + } + + if (rval == 0L) { + // x + 0 = x + // x - 0 = x + // x | 0 = x + // x * 0 = 0 + // x & 0 = 0 + switch (expr.operation()) { + case ArithExpr.ADD: + case ArithExpr.SUB: + case ArithExpr.IOR: + node = expr.left(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.MUL: + case ArithExpr.AND: + node = new ConstantExpr(new Long(0L), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + } else if (rval == 1L) { + // x * 1 = x + // x / 1 = x + // x % 1 = 0 + switch (expr.operation()) { + case ArithExpr.MUL: + case ArithExpr.DIV: + node = expr.left(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.REM: + node = new ConstantExpr(new Long(0L), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + } else if (rval == -1L) { + // x * -1 = -x + // x / -1 = -x + switch (expr.operation()) { + case ArithExpr.MUL: + case ArithExpr.DIV: + if (replace) { + expr.left().setParent(null); + node = new NegExpr(expr.left(), Type.LONG); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.right().cleanup(); + expr.cleanupOnly(); + } else { + node = new NegExpr((Expr) expr.left().clone(), + Type.LONG); + node.setValueNumber(v); + clean.add(node); + } + break; + } + } + } else if (lexpr != null && rexpr == null) { + long lval = ((Long) lexpr.value()).longValue(); + if (lval == 0L) { + // 0 + x = x + // 0 - x = -x + // 0 | x = x + // 0 * x = 0 + // 0 & x = 0 + switch (expr.operation()) { + case ArithExpr.ADD: + case ArithExpr.IOR: + node = expr.right(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } + break; + case ArithExpr.SUB: + if (replace) { + expr.right().setParent(null); + node = new NegExpr(expr.right(), Type.LONG); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } else { + node = new NegExpr((Expr) expr.right().clone(), + Type.LONG); + node.setValueNumber(v); + clean.add(node); + } + break; + case ArithExpr.MUL: + case ArithExpr.AND: + node = new ConstantExpr(new Long(0L), expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + break; + } + } else if (lval == 1L) { + // 1 * x = x + switch (expr.operation()) { + case ArithExpr.MUL: + node = expr.right(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } + break; + } + } else if (lval == -1L) { + // -1 * x = -x + switch (expr.operation()) { + case ArithExpr.MUL: + if (replace) { + expr.right().setParent(null); + node = new NegExpr(expr.right(), Type.LONG); + node.setValueNumber(v); + expr.replaceWith(node, false); + expr.left().cleanup(); + expr.cleanupOnly(); + } else { + node = new NegExpr((Expr) expr.right().clone(), + Type.LONG); + node.setValueNumber(v); + clean.add(node); + } + break; + } + } + } + } + + /** + * Look for float arithmetic identities... + */ + private void foldArithFloat(ArithExpr expr) { + int v = expr.valueNumber(); + int lv = expr.left().valueNumber(); + int rv = expr.right().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + ConstantExpr lexpr = null; + ConstantExpr rexpr = null; + + if (0 <= lv && lv < values.size()) { + lexpr = (ConstantExpr) values.get(lv); + } + + if (0 <= rv && rv < values.size()) { + rexpr = (ConstantExpr) values.get(rv); + } + + if (lexpr == null || rexpr == null) { + return; + } + + Float rvalue = (Float) rexpr.value(); + Float lvalue = (Float) lexpr.value(); + + if (lvalue.isNaN() || lvalue.isInfinite()) { + return; + } + + if (rvalue.isNaN() || rvalue.isInfinite()) { + return; + } + + boolean leffects = false; + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + expr.left().visit(sideEffects); + leffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + expr.right().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + + if (leffects || reffects) { + return; + } + } + + // Can't fold (x op C) = y since x may be NaN or infinite + // or +/-0.0. + + Number value = null; + + float lval = lvalue.floatValue(); + float rval = rvalue.floatValue(); + + switch (expr.operation()) { + case ArithExpr.ADD: + value = new Float(lval + rval); + break; + case ArithExpr.DIV: + value = new Float(lval / rval); + break; + case ArithExpr.MUL: + value = new Float(lval * rval); + break; + case ArithExpr.REM: + value = new Float(lval % rval); + break; + case ArithExpr.SUB: + value = new Float(lval - rval); + break; + case ArithExpr.CMP: + if (lval > rval) { + value = new Integer(1); + } else if (lval < rval) { + value = new Integer(-1); + } else { + value = new Integer(0); + } + break; + default: + break; + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + } + + /** + * Look for double arithmetic identities... + */ + private void foldArithDouble(ArithExpr expr) { + int v = expr.valueNumber(); + int lv = expr.left().valueNumber(); + int rv = expr.right().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + ConstantExpr lexpr = null; + ConstantExpr rexpr = null; + + if (0 <= lv && lv < values.size()) { + lexpr = (ConstantExpr) values.get(lv); + } + + if (0 <= rv && rv < values.size()) { + rexpr = (ConstantExpr) values.get(rv); + } + + if (lexpr == null || rexpr == null) { + return; + } + + Double rvalue = (Double) rexpr.value(); + Double lvalue = (Double) lexpr.value(); + + if (lvalue.isNaN() || lvalue.isInfinite()) { + return; + } + + if (rvalue.isNaN() || rvalue.isInfinite()) { + return; + } + + boolean leffects = false; + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + expr.left().visit(sideEffects); + leffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + expr.right().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + + if (leffects || reffects) { + return; + } + } + + // Can't fold (x op C) = y since x may be NaN or infinite + // or +/-0.0. + + Number value = null; + + double lval = lvalue.doubleValue(); + double rval = rvalue.doubleValue(); + + switch (expr.operation()) { + case ArithExpr.ADD: + value = new Double(lval + rval); + break; + case ArithExpr.DIV: + value = new Double(lval / rval); + break; + case ArithExpr.MUL: + value = new Double(lval * rval); + break; + case ArithExpr.REM: + value = new Double(lval % rval); + break; + case ArithExpr.SUB: + value = new Double(lval - rval); + break; + case ArithExpr.CMP: + if (lval > rval) { + value = new Integer(1); + } else if (lval < rval) { + value = new Integer(-1); + } else { + value = new Integer(0); + } + break; + default: + break; + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + } + + public void visitCastExpr(CastExpr expr) { + // Note: we can't fold i2b, i2c, i2s, i2l, i2f, f2i, ... + // We only fold (String) "" and (C) null. + + int v = expr.valueNumber(); + int ev = expr.expr().valueNumber(); + values.ensureSize(Math.max(v, ev) + 1); + + ConstantExpr eexpr = null; + + if (0 <= ev && ev < values.size()) { + eexpr = (ConstantExpr) values.get(ev); + } + + if (eexpr == null) { + return; + } + + if (replace) { + sideEffects.reset(); + expr.expr().visit(sideEffects); + boolean effects = sideEffects.hasSideEffects(); + + if (effects) { + return; + } + } + + Object evalue = eexpr.value(); + + if (evalue instanceof String && expr.castType().equals(Type.STRING)) { + // The ConstantExpr must be "" + node = new ConstantExpr(evalue, expr.castType()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + + return; + } + + if (expr.castType() instanceof ReferenceType) { + if (evalue == null && expr.castType() instanceof ReferenceType) { + // The ConstantExpr is null + node = new ConstantExpr(null, expr.castType()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + + return; + } + } + + public void visitNegExpr(NegExpr expr) { + int v = expr.valueNumber(); + int ev = expr.expr().valueNumber(); + values.ensureSize(Math.max(v, ev) + 1); + + ConstantExpr eexpr = null; + + if (0 <= ev && ev < values.size()) { + eexpr = (ConstantExpr) values.get(ev); + } + + if (eexpr != null) { + // If the operand of the NegExpr is a constant value, simply + // replace the constant with its negation and remove the + // NegExpr. + + Number evalue = (Number) eexpr.value(); + + boolean eeffects = false; + + if (replace) { + sideEffects.reset(); + expr.expr().visit(sideEffects); + eeffects = sideEffects.hasSideEffects(); + } + + if (!eeffects) { + Number value = null; + + if (evalue instanceof Integer) { + value = new Integer(-evalue.intValue()); + } else if (value instanceof Long) { + value = new Long(-evalue.longValue()); + } else if (value instanceof Float) { + value = new Float(-evalue.floatValue()); + } else if (value instanceof Double) { + value = new Double(-evalue.doubleValue()); + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + + return; + } + } + } + + if (expr.expr() instanceof NegExpr) { + // -(-x) --> x + + NegExpr neg = (NegExpr) expr.expr(); + node = neg.expr(); + + if (replace) { + expr.parent().visit(new ReplaceVisitor(expr, node)); + expr.cleanupOnly(); + neg.cleanupOnly(); + } + } + } + + public void visitShiftExpr(ShiftExpr expr) { + // Exploit shifting zero bits or shifting zero + + int v = expr.valueNumber(); + int ev = expr.expr().valueNumber(); + int bv = expr.bits().valueNumber(); + + int max = v; + max = Math.max(max, ev); + max = Math.max(max, bv); + values.ensureSize(max + 1); + + ConstantExpr eexpr = null; + ConstantExpr bexpr = null; + + if (0 <= ev && ev < values.size()) { + eexpr = (ConstantExpr) values.get(ev); + } + + if (0 <= bv && bv < values.size()) { + bexpr = (ConstantExpr) values.get(bv); + } + + Object evalue = null; + Object bvalue = null; + boolean eeffects = false; + boolean beffects = false; + + if (eexpr != null) { + evalue = eexpr.value(); + } + + if (bexpr != null) { + bvalue = bexpr.value(); + } + + if (replace) { + sideEffects.reset(); + expr.expr().visit(sideEffects); + eeffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + expr.bits().visit(sideEffects); + beffects = sideEffects.hasSideEffects(); + } + + if (eexpr == null && bexpr != null) { + if (bvalue.equals(new Integer(0)) || bvalue.equals(new Long(0))) { + // x << 0 = x + // x >> 0 = x + // x >>> 0 = x + if (!beffects) { + node = expr.expr(); + + if (replace) { + node.setParent(null); + expr.replaceWith(node, false); + expr.bits().cleanup(); + expr.cleanupOnly(); + } + } + } + + return; + } + + if (beffects) { + return; + } + + Object value = null; + + if (evalue instanceof Integer) { + int eval = ((Integer) evalue).intValue(); + + if (eval == 0) { + // 0 << x = 0 + // 0 >> x = 0 + // 0 >>> x = 0 + value = evalue; + + } else if (bvalue instanceof Integer) { + if (replace && eeffects) { + return; + } + + int bval = ((Integer) bvalue).intValue(); + + switch (expr.dir()) { + case ShiftExpr.LEFT: + value = new Integer(eval << bval); + break; + case ShiftExpr.RIGHT: + value = new Integer(eval >> bval); + break; + case ShiftExpr.UNSIGNED_RIGHT: + value = new Integer(eval >>> bval); + break; + } + } + + } else if (evalue instanceof Long) { + long eval = ((Long) evalue).longValue(); + + if (eval == 0) { + // 0 << x = 0 + // 0 >> x = 0 + // 0 >>> x = 0 + value = evalue; + + } else if (bvalue instanceof Integer) { + if (replace && eeffects) { + return; + } + + int bval = ((Integer) bvalue).intValue(); + + switch (expr.dir()) { + case ShiftExpr.LEFT: + value = new Long(eval << bval); + break; + case ShiftExpr.RIGHT: + value = new Long(eval >> bval); + break; + case ShiftExpr.UNSIGNED_RIGHT: + value = new Long(eval >>> bval); + break; + } + } + } + + if (value != null) { + node = new ConstantExpr(value, expr.type()); + node.setValueNumber(v); + + values.set(v, node); + + if (replace) { + expr.replaceWith(node); + } + } + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + // If the expression being compared to zero evaluates to a + // constant, then try to exploit this fact. + + Block block = stmt.block(); + FlowGraph cfg = block.graph(); + + int v = stmt.valueNumber(); + int ev = stmt.expr().valueNumber(); + values.ensureSize(Math.max(ev, v) + 1); + + ConstantExpr eexpr = null; + + if (0 <= ev && ev < values.size()) { + eexpr = (ConstantExpr) values.get(ev); + } + + if (eexpr == null) { + return; + } + + Object evalue = eexpr.value(); + + boolean eeffects = false; + + if (replace) { + sideEffects.reset(); + stmt.expr().visit(sideEffects); + eeffects = sideEffects.hasSideEffects(); + + if (eeffects) { + return; + } + } + + JumpStmt jump; + + if (evalue instanceof Integer) { + int eval = ((Integer) evalue).intValue(); + + boolean result; + + switch (stmt.comparison()) { + case IfStmt.EQ: + result = eval == 0; + break; + case IfStmt.NE: + result = eval != 0; + break; + case IfStmt.GT: + result = eval > 0; + break; + case IfStmt.GE: + result = eval >= 0; + break; + case IfStmt.LT: + result = eval < 0; + break; + case IfStmt.LE: + result = eval <= 0; + break; + default: + throw new RuntimeException(); + } + + if (result) { + // Result is always true, replace IfZeroStmt with an + // unconditional jump to the true target. + jump = new GotoStmt(stmt.trueTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.falseTarget()); + } + + } else { + // Result is always false, replace IfZeroStmt with an + // unconditional jump to the false target. + jump = new GotoStmt(stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.trueTarget()); + } + } + + } else if (evalue == null) { + // The expression always evaluates to null + + switch (stmt.comparison()) { + case IfStmt.EQ: + // Always jump to true target + jump = new GotoStmt(stmt.trueTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.falseTarget()); + } + break; + + case IfStmt.NE: + // Always jump to false target + jump = new GotoStmt(stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.trueTarget()); + } + break; + default: + throw new RuntimeException(); + } + } + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + Block block = stmt.block(); + FlowGraph cfg = block.graph(); + + int v = stmt.valueNumber(); + int lv = stmt.left().valueNumber(); + int rv = stmt.right().valueNumber(); + + int max = v; + max = Math.max(max, lv); + max = Math.max(max, rv); + values.ensureSize(max + 1); + + ConstantExpr lexpr = null; + ConstantExpr rexpr = null; + + if (0 <= lv && lv < values.size()) { + lexpr = (ConstantExpr) values.get(lv); + } + + if (0 <= rv && rv < values.size()) { + rexpr = (ConstantExpr) values.get(rv); + } + + Object lvalue = null; + Object rvalue = null; + + if (lexpr != null) { + lvalue = lexpr.value(); + } + + if (rexpr != null) { + rvalue = rexpr.value(); + } + + boolean leffects = false; + boolean reffects = false; + + if (replace) { + sideEffects.reset(); + stmt.left().visit(sideEffects); + leffects = sideEffects.hasSideEffects(); + + sideEffects.reset(); + stmt.right().visit(sideEffects); + reffects = sideEffects.hasSideEffects(); + } + + if (lvalue instanceof Integer && !leffects) { + int lval = ((Integer) lvalue).intValue(); + + if (lval == 0 && !((rvalue instanceof Integer) || reffects)) { + // If two integers are being compared and the left operand is + // zero, then we can replace the IfCmpStmt with a IfZeroStmt. + + int cmp; + + switch (stmt.comparison()) { + case IfStmt.EQ: + cmp = IfStmt.EQ; + break; + case IfStmt.NE: + cmp = IfStmt.NE; + break; + case IfStmt.LT: + cmp = IfStmt.GT; + break; + case IfStmt.LE: + cmp = IfStmt.GE; + break; + case IfStmt.GT: + cmp = IfStmt.LT; + break; + case IfStmt.GE: + cmp = IfStmt.LE; + break; + default: + throw new RuntimeException(); + } + + if (replace) { + JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt.right() + .clone(), stmt.trueTarget(), stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + stmt.replaceWith(node); + + } else { + // Why bother! -- Nate + // Why ask why! -- Dave + node = null; + } + + return; + } + } + + if (rvalue instanceof Integer && !reffects) { + int rval = ((Integer) rvalue).intValue(); + + if (rval == 0 && !((lvalue instanceof Integer) || leffects)) { + // If IfCmpStmt compares two integers and the right operand is + // zero, then replace the IfCmpStmt with an IfZeroStmt. + int cmp = stmt.comparison(); + + if (replace) { + JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt.left() + .clone(), stmt.trueTarget(), stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + stmt.replaceWith(node); + + } else { + // Why bother! -- Cut and paste! Way to go Nate! + node = null; + } + + return; + } + } + + if (lexpr != null && lvalue == null && !leffects) { + if (rexpr == null || rvalue != null || reffects) { + // Left operand evaluates to null. Replace IfCmpStmt with an + // IfZeroStmt. + int cmp = stmt.comparison(); + + if (replace) { + JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt.right() + .clone(), stmt.trueTarget(), stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + stmt.replaceWith(node); + + } else { + // Why bother! + node = null; + } + + return; + } + } + + if (rexpr != null && rvalue == null && !reffects) { + if (lexpr == null || lvalue != null || leffects) { + // The right operand evaluates to null. Replace IfCmpStmt + // with an IfZeroStmt. Note that we do not need to mess with + // operators because if the lhs is being compared against + // null, it must be a reference type and the only operators + // are EQ and NE. + + int cmp = stmt.comparison(); + + if (replace) { + JumpStmt jump = new IfZeroStmt(cmp, (Expr) stmt.left() + .clone(), stmt.trueTarget(), stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + stmt.replaceWith(node); + + } else { + // Why bother! + node = null; + } + + return; + } + } + + if (leffects || reffects) { + return; + } + + if (lexpr == null || rexpr == null) { + return; + } + + JumpStmt jump; + + if (lvalue instanceof Integer && rvalue instanceof Integer) { + // Both operands evaluate to non-zero integers, evaluate the + // comparison and go from there. + + int lval = ((Integer) lvalue).intValue(); + int rval = ((Integer) rvalue).intValue(); + + boolean result; + + switch (stmt.comparison()) { + case IfStmt.EQ: + result = lval == rval; + break; + case IfStmt.NE: + result = lval != rval; + break; + case IfStmt.GT: + result = lval > rval; + break; + case IfStmt.GE: + result = lval >= rval; + break; + case IfStmt.LT: + result = lval < rval; + break; + case IfStmt.LE: + result = lval <= rval; + break; + default: + throw new RuntimeException(); + } + + if (result) { + jump = new GotoStmt(stmt.trueTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.falseTarget()); + } + + } else { + jump = new GotoStmt(stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.trueTarget()); + } + } + + } else if (lvalue == null && rvalue == null) { + switch (stmt.comparison()) { + case IfStmt.EQ: + jump = new GotoStmt(stmt.trueTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.falseTarget()); + } + break; + case IfStmt.NE: + jump = new GotoStmt(stmt.falseTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + cfg.removeEdge(block, stmt.trueTarget()); + } + break; + default: + throw new RuntimeException(); + } + } + } + + public void visitSwitchStmt(SwitchStmt stmt) { + // If the index of the SwitchStmt evaluates to a constant value, + // then always take that target (may be the default target). + // Replace the SwitchStmt with a GotoStmt. + Block block = stmt.block(); + FlowGraph cfg = block.graph(); + + int v = stmt.valueNumber(); + int iv = stmt.index().valueNumber(); + values.ensureSize(Math.max(v, iv) + 1); + + ConstantExpr iexpr = null; + + if (0 <= iv && iv < values.size()) { + iexpr = (ConstantExpr) values.get(iv); + } + + boolean ieffects = false; + + if (replace) { + sideEffects.reset(); + stmt.index().visit(sideEffects); + ieffects = sideEffects.hasSideEffects(); + + if (ieffects) { + return; + } + } + + if (iexpr == null) { + return; + } + + if (!(iexpr.value() instanceof Integer)) { + return; + } + + JumpStmt jump; + + Integer ivalue = (Integer) iexpr.value(); + + int ival = ivalue.intValue(); + + boolean useDefault = true; + + for (int i = 0; i < stmt.values().length; i++) { + if (stmt.values()[i] == ival) { + jump = new GotoStmt(stmt.targets()[i]); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + } + useDefault = false; + + } else { + // Definitely not to this target. + if (replace) { + cfg.removeEdge(block, stmt.targets()[i]); + } + } + } + + if (useDefault) { + jump = new GotoStmt(stmt.defaultTarget()); + jump.catchTargets().addAll(stmt.catchTargets()); + node = jump; + node.setValueNumber(v); + + if (replace) { + stmt.replaceWith(node); + } + } else { + // Definitely not to the default target. + if (replace) { + cfg.removeEdge(block, stmt.defaultTarget()); + } + } + } + + void printValueNumbers(PrintWriter pw) { + if (pw == null) + return; + + Iterator iter = values.iterator(); + + pw.println("Value Numbers mapped to constants\n"); + + for (int i = 0; iter.hasNext(); i++) { + Object o = iter.next(); + if (o != null) + pw.println(i + " -> " + o); + } + } +} diff --git a/src/edu/purdue/cs/bloat/trans/ValueFolding.java b/src/edu/purdue/cs/bloat/trans/ValueFolding.java new file mode 100644 index 0000000..b482030 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/ValueFolding.java @@ -0,0 +1,306 @@ +/* + * Class: ValueFolding + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import java.io.*; +import java.util.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.editor.EditorContext; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.ssa.ComponentVisitor; +import edu.purdue.cs.bloat.ssa.SSAGraph; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.Expr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiCatchStmt; + +/** + * ValueFolding uses a ValueFolder to determine which + * nodes in an expression tree can be removed or replaced by common expression + * elimination and constant propagation. + * + * @see ValueFolder + */ +public class ValueFolding implements Optimization { + public static boolean DEBUG = false; + + SideEffectChecker sideEffects; + + ValueFolder folder; + + boolean changed; + + public static boolean DUMP = false; + + public static boolean SAVEDUMP = false; + + PrintWriter vn; + + PrintWriter dump; + + /** + * Performs value folding (common expression elimination and constant + * folding) on a control flow graph. + */ + int next = 1; + + public void transform(MethodState state) { + FlowGraph cfg = state.controlFlowGraph(); + + if (DUMP) { + vn = new PrintWriter(System.out, true); + dump = new PrintWriter(System.out, true); + } + + // MAY NOT BE THE BEST THING TO DO + // Repository context = cfg.method().declaringClass().context(); + // Repository context = org.apache.bcel.Repository.getRepository(); + // EditorContext context = new + // PersistentBloatContext(org.apache.bcel.Repository.getRepository()); + + // Get the context from the MethodState object. In theory, this may + // cause + // problems if PersistentBloatContext.closure starts meaning something, + // so you may have to return to the previous definition. But this way is + // much nicer. + EditorContext context = state.context(); + + sideEffects = new SideEffectChecker(context); + + folder = new ValueFolder(true, context); + + SSAGraph ssaGraph = new SSAGraph(cfg); + + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(List scc) { + // Maps Nodes in the SCC to their folded value + LinkedHashMap map = new LinkedHashMap(scc.size() * 2 + 1); + + boolean changed = true; + + while (changed) { + changed = false; + + Iterator iter = scc.iterator(); + + int x = 0; + while (iter.hasNext()) { + Node node = (Node) iter.next(); + + if (DUMP) { + x++; + dump.println("Folding SCC Node " + node + " (" + x + + " of " + scc.size() + ")"); + } + + if (fold(map, node)) { + changed = true; + } + } + + if (DUMP) + dump.println(""); + + if (scc.size() == 1) { + break; + } + } + } + }); + + cfg.removeUnreachable(); + + folder = null; + sideEffects = null; + + // Okay, we've successfully value folded, remove debugging files + } + + /** + * Builds a mapping between the nodes in a CFG's SCCs and the expressions + * they are equivalent to. + * + * @param map + * A mapping between the SCCs of the CFG's SSA Graph + * (definitions) and the expressions they are folded to + * @param sscNode + * A Node in the SCC of the SSA Graph + * + * @return True, if the value in the mapping was changed. + */ + boolean fold(Map map, Node sccNode) { + Node node = (Node) map.get(sccNode); + + if (DUMP) + dump + .println(" SCC Node " + sccNode + " is mapped to node " + + node); + + // The SCC node has not been folded yet, fold it to itself + if (node == null) { + node = sccNode; + } + + if (!node.hasParent()) { + return false; + } + + if (DEBUG) { + System.out.println("folding --- " + node + " in " + node.parent()); + } + + if (DUMP) { + dump.println(" Folding " + node + " (" + "VN=" + + node.valueNumber() + ") in " + node.parent()); + } + + int v = node.valueNumber(); + + if (v == -1) { + // Node has not been assigned a value number, can't do anything + return false; + } + + folder.values.ensureSize(v + 1); + ConstantExpr oldValue = (ConstantExpr) folder.values.get(v); + ConstantExpr value = null; + + if (DUMP) + dump.println(" Node " + node + " is mapped to constant " + + oldValue); + + if (node instanceof ConstantExpr) { + // If the node that we're dealing with is already a + // ConstantExpr, change it to the mapped value if it is + // different. + value = (ConstantExpr) node; + + if (oldValue == null || !oldValue.equalsExpr(value)) { + // The node was not previously mapped to a constant, or it was + // mapped to a different constant. Update the mapping to + // relfect the new constant. + if (DEBUG) { + System.out.println("changed " + oldValue + " to " + value); + } + + if (DUMP) { + dump.println(" Changed " + oldValue + " to " + value); + } + + folder.values.set(v, value); + return true; + } + + // Mapping was already correct, don't do anything. + return false; + } + + if (node instanceof Expr && oldValue != null) { + // The node is a non-constant Expr that was mapped to a constant + + if (node.parent() instanceof PhiCatchStmt) { + // Don't fold values inside PhiCatchStmts + return false; + } + + sideEffects.reset(); + node.visit(sideEffects); + + if (!sideEffects.hasSideEffects()) { + // If the expression does not have side effects, then make a + // clone of the value to which it was mapped and map the clone + // to the original sccNode (which may or may not be node). + // Technically, the mapping did not change. + + value = (ConstantExpr) oldValue.clone(); + node.replaceWith(value); + map.put(sccNode, value); + return false; + } + } + + if (value == null) { + // The node is mapped to nothing, Use the ValueFolder to + // determine a expression that node can be folded to. + + folder.node = null; + node.visit(folder); + + if (DEBUG) { + System.out.println("folded " + node + " to " + folder.node); + } + + if (DUMP) { + dump.println(" Using ValueFolder to determine new value"); + dump.println(" Folded " + node + " to " + folder.node); + } + + if (folder.node != null) { + // Assert.isTrue(folder.node.hasParent(), + // "No parent for " + folder.node); + map.put(sccNode, folder.node); + } + + if (folder.node instanceof ConstantExpr) { + // If the node was folded into a ConstantExpr, then fold it in + // the ValueFolder. + value = (ConstantExpr) folder.node; + folder.values.set(v, value); + return true; + } + } + + return false; + } + + public String traceMessage(String dateString) { + return " Value Folding: " + dateString; + } + + public String preDebugMessage() { + return "--------Before Value Folding---------"; + } + + public String postDebugMessage() { + return "---------After Value Folding---------"; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/ValueNumbering.java b/src/edu/purdue/cs/bloat/trans/ValueNumbering.java new file mode 100644 index 0000000..427c3de --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/ValueNumbering.java @@ -0,0 +1,536 @@ +/* + * Class: ValueNumbering + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.trans; + +import java.util.*; +import java.io.*; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.ssa.ComponentVisitor; +import edu.purdue.cs.bloat.ssa.SSAGraph; +import edu.purdue.cs.bloat.tree.ConstantExpr; +import edu.purdue.cs.bloat.tree.MemRefExpr; +import edu.purdue.cs.bloat.tree.Node; +import edu.purdue.cs.bloat.tree.PhiStmt; +import edu.purdue.cs.bloat.tree.Tree; +import edu.purdue.cs.bloat.tree.TreeVisitor; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Performs value numbering analysis on the nodes in a control flow graph. Nodes + * with identical value numbers are folded into one another so that common + * (redundent) expressions are eliminated. Note that ValueNumbering works on the + * SSAGraph for the CFG and not the CFG itself. + * + * @see SSAGraph + */ +// L.T. Simpson 1996. Value-driven redundancy elimination. PhD +// Thesis. Rice University. Look it up. Chapter 4 contains the +// stuff on SCC-based value numbering. +public class ValueNumbering implements Optimization { + public static boolean DEBUG = false; + + SSAGraph ssaGraph; + + LinkedHashMap tuples; // Maps a Node to its Tuple + + ValueFolder folder; + + int next; // The next value number to assign + + public String debugDirName = "debug"; + + public File debugDir; + + public boolean DUMP = false; + + private PrintWriter dump = new PrintWriter(System.out); + + /** + * Performs value numbering on a control flow graph. + * + * @see ComponentVisitor + * @see SSAGraph + * @see ValueFolder + */ + public void transform(MethodState state) { + FlowGraph cfg = state.controlFlowGraph(); + + // Specify directory into which all debugging files should be + // placed + + if (DUMP || ValueFolding.DUMP) { + String className = cfg.method().getClassName(); + String methodName = cfg.method().getName(); + + String dirName = debugDirName + File.separator + className + + File.separator + methodName; + + debugDir = new File(dirName); + for (int nextDir = 1; debugDir.exists(); nextDir++) { + // Multiple directories + debugDir = new File(dirName + "_" + nextDir); + } + + if (!debugDir.exists()) + debugDir.mkdirs(); + + // General dumping file + try { + File dumpFile = new File(debugDir, "vn_dump"); + dump = new PrintWriter(new FileWriter(dumpFile), true); + } catch (IOException ex) { + System.err.println(ex.toString()); + } + } + + ssaGraph = new SSAGraph(cfg); + tuples = new LinkedHashMap(); + + // Use state.context() to get the EditorContext. It breaks, go back to + // the + // "new PersistentBloatContext(...) version. + folder = new ValueFolder(false, state.context()); + // new + // PersistentBloatContext(org.apache.bcel.Repository.getRepository())); + // cfg.method().declaringClass().context()); + // AGAIN THIS MIGHT NOT BE THE BEST + next = 0; + + final LinkedHashMap valid = new LinkedHashMap(); + final LinkedHashMap optimistic = new LinkedHashMap(); + + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(List scc) { + if (DEBUG || DUMP) { + dump.println("\nNumbering SCC = " + scc); + } + + Iterator e = scc.iterator(); + + while (e.hasNext()) { + Node node = (Node) e.next(); + node.setValueNumber(-1); + } + + if (scc.size() > 1) { + if (DEBUG || DUMP) { + dump.println("Optimistic-----------------------"); + } + + boolean changed = true; + + while (changed) { + changed = false; + + Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + Node node = (Node) iter.next(); + + if (valnum(node, optimistic)) { + changed = true; + } + } + } + } + + if (DEBUG || DUMP) { + dump.println("Valid--------------------------------"); + } + + // The valid table contains the correct value numbers. Run + // through the each node in the SCC and call valnum. + // Presumably, the nodes are in reverse postorder. + Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + Node node = (Node) iter.next(); + valnum(node, valid); + } + } + }); + + if (DEBUG || DUMP) { + dump.println("Final value numbers--------------------------"); + printValueNumbers(cfg, new PrintWriter(dump)); + } + + if (DUMP) { + System.out.println(" Dumping to: " + debugDir); + + try { + File valueNumbers = new File(debugDir, "scc.txt"); + ssaGraph + .printSCCs(new PrintWriter(new FileWriter(valueNumbers))); + } catch (IOException ex) { + System.err.println("IOException: " + ex); + } + } + + ssaGraph = null; + tuples = null; + + folder.cleanup(); + folder = null; + + // Make sure each node has a value number + cfg.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(Node node) { + node.visitChildren(this); + Assert.isTrue(node.valueNumber() != -1, "No value number for " + + node); + } + }); + } + + private void printValueNumbers(FlowGraph cfg, final PrintWriter pw) { + cfg.visit(new TreeVisitor() { + public void visitTree(Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(Node node) { + node.visitChildren(this); + + pw.println("VN[" + node + " " + System.identityHashCode(node) + + // " == " + ssaGraph.equivalent(node) + " " + + // System.identityHashCode(ssaGraph.equivalent(node)) + + "] = " + node.valueNumber()); + } + }); + } + + /** + * Simplifies a node by examining its type. A ValueFolder may be used to + * perform simplification. + * + * @return The folded (simplified) value of the node (which may be the same + * as the node itself) + */ + private Node simplify(Node node) { + if (DEBUG || DUMP) { + dump.println("folding " + node + " in " + node.parent()); + } + + int v = node.valueNumber(); + + // A value number of -1 (i.e. value number has not yet been + // assigned) cannot be simplified. + if (v == -1) { + return node; + } + + // A constant expression can't be simplified, set the value of + // value number v to be node + if (node instanceof ConstantExpr) { + folder.values.ensureSize(v + 1); + folder.values.set(v, node); + return node; + } + + // Check for the value number in the folder. + if (v < folder.values.size()) { + ConstantExpr value = (ConstantExpr) folder.values.get(v); + + if (value != null) { + return value; + } + } + + // Else, use a ValueFolder to fold the node + folder.node = null; + node.visit(folder); + + if (DEBUG || DUMP) { + dump.println("folded " + node + " to " + folder.node); + } + + if (folder.node == null) { + // Nothing changed + return node; + } + + // If we folded the node into a constant expression, add it to + // the values list + if (folder.node instanceof ConstantExpr) { + folder.values.ensureSize(v + 1); + folder.values.set(v, folder.node); + } + + return folder.node; + } + + /** + * Processes a Node in an SCC. + */ + private boolean valnum(Node node, LinkedHashMap table) { + boolean changed = false; // Did the table change? + + Tuple tuple = (Tuple) tuples.get(node); + + if (tuple == null) { + // Make a new Tuple for the node being processed + Node s = simplify(node); + + tuple = new Tuple(s); + tuples.put(node, tuple); + + if (DEBUG || DUMP) { + dump.println(" New tuple " + tuple); + } + + } else if (DUMP) { + dump.println(" " + node + " mapped to tuple " + tuple); + } + + Node w = (Node) table.get(tuple); + + if (DEBUG || DUMP) { + dump.println(" Looking up " + tuple); + dump.println(" " + tuple + " mapped to node " + w + + (w != null ? " (VN = " + w.valueNumber() + ")" : "")); + } + + int value = -1; + + if (w != null && w.valueNumber() != -1) { + value = w.valueNumber(); + + } else { + if (DEBUG || DUMP) { + dump.println(" New value number " + next); + } + + value = next++; + } + + Assert.isTrue(value != -1); + + // Now make sure all equivalent nodes have the same value number. + Iterator iter = ssaGraph.equivalent(node).iterator(); + + while (iter.hasNext()) { + Node v = (Node) iter.next(); + + Tuple t = (Tuple) tuples.get(v); + + if (t == null) { + // Will get done later. + continue; + } + + if (v.valueNumber() != value) { + v.setValueNumber(value); + table.put(t, v); + + if (DEBUG || DUMP) { + dump.println(" Assigning value number " + + v.valueNumber() + " to " + v); + dump.println(" Mapping tuple " + t + " to node " + v); + } + + changed = true; + } + } + + return changed; + } + + /** + * Tuple contains a Node and an associated hash value. The Node is the + * simplified version of another node. The main purpose of the Tuple class + * is to compare two Nodes to determine if they are the same with respect to + * their value numbers. + */ + class Tuple { + Node node; + + int hash; + + public Tuple(Node node) { + this.node = node; + List children = ssaGraph.children(node); + this.hash = NodeComparator.hashCode(node) + children.size(); + } + + public String toString() { + List children = ssaGraph.children(node); + + String s = "<" + node + ", hash=" + hash; + + Iterator iter = children.iterator(); + + while (iter.hasNext()) { + Node child = (Node) iter.next(); + s += ", " + child + "{" + child.valueNumber() + "}"; + } + + s += ">"; + + return s; + } + + public int hashCode() { + return hash; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof Tuple) { + Tuple t = (Tuple) obj; + + if (node == t.node) { + return true; + } + + // All mem refs are unequal. + if (node instanceof MemRefExpr || t.node instanceof MemRefExpr) { + return false; + } + + if (!NodeComparator.equals(node, t.node)) { + return false; + } + + List children1 = ssaGraph.children(node); + List children2 = ssaGraph.children(t.node); + + if (children1.size() != children2.size()) { + return false; + } + + if (node instanceof PhiStmt) { + // The order of the children does not matter + int[] used = new int[next]; + int free = 0; // The number of un-numbered children + + Iterator iter = children1.iterator(); + + while (iter.hasNext()) { + Node child = (Node) iter.next(); + int v = child.valueNumber(); + + if (v != -1) { + used[v]++; + + } else { + free++; + } + } + + iter = children2.iterator(); + + while (iter.hasNext()) { + Node child = (Node) iter.next(); + int v = child.valueNumber(); + + if (v != -1) { + if (used[v] != 0) { + used[v]--; + + } else { + free--; + } + + } else { + free--; + } + } + + if (free < 0) { + return false; + } + + return true; + + } else { + // The children of the nodes in the SSAGraph must have + // the + // same value numbers and be in the same order. + Iterator iter1 = children1.iterator(); + Iterator iter2 = children2.iterator(); + + while (iter1.hasNext() && iter2.hasNext()) { + Node child1 = (Node) iter1.next(); + Node child2 = (Node) iter2.next(); + + int v1 = child1.valueNumber(); + int v2 = child2.valueNumber(); + + if (v1 != -1 && v2 != -1 && v1 != v2) { + return false; + } + } + + if (iter1.hasNext() || iter2.hasNext()) { + // Size mismatch. + return false; + } + + return true; + } + } + + return false; + } + } + + public String traceMessage(String dateString) { + return " Value Numbering: " + dateString; + } + + public String preDebugMessage() { + return "--------Doing value numbering--------"; + } + + public String postDebugMessage() { + return null; + } +} diff --git a/src/edu/purdue/cs/bloat/trans/package.html b/src/edu/purdue/cs/bloat/trans/package.html new file mode 100644 index 0000000..ee63621 --- /dev/null +++ b/src/edu/purdue/cs/bloat/trans/package.html @@ -0,0 +1,45 @@ + + + +

      Performs transformations (optimizations) on a control flow graph. +There are several optimizations performed by classes in this package. +Older Java compilers produced poor code for initializing arrays. +BLOAT can replace this poor code with more efficient code by loading +the array from the constant pool. Dead code elimination removes code +from a method that does not contribute to the final output of the +program.

      + +

      Value numbering associates a number with each expression such that +if two expressions have the same number, they have the same value. A +value folder is then used to eliminate redundent nodes from and to +propagate constants through the control flow graph.

      + +

      Constant propagation removes unnecessary assignments by replacing +variables that are assigned constant values with those values. Copy +propagation removes unnecessary assignments to varibles to other +variables.

      + +

      BLOAT was designed to optimize classes that were to be run inside a +persistent system that required special opcodes to perform operation +such as checking an object cache for a certain resident object. The +analysis that BLOAT does can eliminate some of these checks.

      + +

      SSA-based partial redundency elimination (PRE) of expressions and +access paths can also be performed on a control flow graph. If a +calculation is redundent along one control flow path leading to a +merge point, PRE computes it once along all paths, assigns the +result to a variable, and replaces further occurrences of the +calculation with that variable. This way, the expression is only +computed once along any given control flow path.

      + +

      Finally, peephole optimizations are performed on Java bytecode. +For instance, a push instruction followed by a pop instruction is +useless and can be removed. Additionally, unreachable code is removed +from the method.

      + +

      This package also contains several auxiliary classes that perform +operations like determining whether or not an expression has side +effects and comparing two nodes.

      + + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/tree/AddressStoreStmt.java b/src/edu/purdue/cs/bloat/tree/AddressStoreStmt.java new file mode 100644 index 0000000..e8923d1 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/AddressStoreStmt.java @@ -0,0 +1,82 @@ +/* + * Class: AddressStoreStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Subroutine; + +/** + * Associated with an AddressStoreStmt is a Subroutine whose address (offset in + * the instruction sequence) is to be stored. Addresses may be loaded (using + * astore), but cannot be reloaded. Therefore, AddressStoreStmt is + * needed to differentiate between a regular (object reference) astore + * which is modeled by a LocalExpr. + * + * @see Tree#visit_astore + * @see Subroutine + * @see LocalExpr + */ +public class AddressStoreStmt extends Stmt { + final Subroutine sub; + + /** + * Constructor. + * + * @param sub + * + */ + public AddressStoreStmt(Subroutine sub) { + this.sub = sub; + } + + public Subroutine sub() { + return sub; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitAddressStoreStmt(this); + } + + public Object clone() { + return copyInto(new AddressStoreStmt(sub)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ArithExpr.java b/src/edu/purdue/cs/bloat/tree/ArithExpr.java new file mode 100644 index 0000000..98032d2 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ArithExpr.java @@ -0,0 +1,146 @@ +/* + * Class: ArithExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ArithExpr represents a binary arithmetic expression. It consists of two + * operands and an operator. + */ +public class ArithExpr extends Expr { + final char operation; // Arithmetic operator + + Expr left; // Expression on left-hand side of operation + + Expr right; // Expression on right-hand side of operation + + // Operators... + public static final char ADD = '+'; + + public static final char SUB = '-'; + + public static final char DIV = '/'; + + public static final char MUL = '*'; + + public static final char REM = '%'; + + public static final char AND = '&'; + + public static final char IOR = '|'; + + public static final char XOR = '^'; + + public static final char CMP = '?'; + + public static final char CMPL = '<'; + + public static final char CMPG = '>'; + + /** + * Constructor. + * + * @param operation + * Arithmetic operation that this expression performs. + * @param left + * Left-hand argument to operation. + * @param right + * Right-hand argument to operation. + * @param type + * The type of this expression. + */ + public ArithExpr(char operation, Expr left, Expr right, Type type) { + super(type); + this.operation = operation; + this.left = left; + this.right = right; + + left.setParent(this); + right.setParent(this); + } + + public int operation() { + return operation; + } + + public Expr left() { + return left; + } + + public Expr right() { + return right; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + right.visit(visitor); + left.visit(visitor); + } else { + left.visit(visitor); + right.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitArithExpr(this); + } + + public int exprHashCode() { + return 1 + operation ^ left.exprHashCode() ^ right.exprHashCode(); + } + + /** + * Compare this arithmetic expression to another Expression. + * + * @return True, if both expressions have the same contents. + */ + public boolean equalsExpr(Expr other) { + return other != null && other instanceof ArithExpr + && ((ArithExpr) other).operation == operation + && ((ArithExpr) other).left.equalsExpr(left) + && ((ArithExpr) other).right.equalsExpr(right); + } + + public Object clone() { + return copyInto(new ArithExpr(operation, (Expr) left.clone(), + (Expr) right.clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ArrayLengthExpr.java b/src/edu/purdue/cs/bloat/tree/ArrayLengthExpr.java new file mode 100644 index 0000000..f40ed49 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ArrayLengthExpr.java @@ -0,0 +1,95 @@ +/* + * Class: ArrayLengthExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * ArrayLengthExpr represents the arraylength opcode which + * gets length of an array. + */ +public class ArrayLengthExpr extends Expr { + Expr array; + + /** + * Constructor. + * + * @param array + * Array whose length is sought. + * @param type + * The type of this expression. + */ + public ArrayLengthExpr(Expr array, Type type) { + super(type); + this.array = array; + array.setParent(this); + } + + public Expr array() { + return array; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + array.visit(visitor); + } else { + array.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitArrayLengthExpr(this); + } + + public int exprHashCode() { + return 3 + array.exprHashCode() ^ (int) Assert.simple(type).hashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof ArrayLengthExpr + && ((ArrayLengthExpr) other).array.equalsExpr(array); + } + + public Object clone() { + return copyInto(new ArrayLengthExpr((Expr) array.clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ArrayRefExpr.java b/src/edu/purdue/cs/bloat/tree/ArrayRefExpr.java new file mode 100644 index 0000000..3777988 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ArrayRefExpr.java @@ -0,0 +1,116 @@ +/* + * Class: ArrayRefExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ArrayRefExpr represents an expression that references an element in an array. + */ +public class ArrayRefExpr extends MemRefExpr { + Expr array; + + Expr index; + + final Type elementType; + + /** + * Constructor. + * + * @param array + * The array whose element we are indexing. + * @param index + * The index into the array. + * @param elementType + * The type of elements in array. + * @param type + * The type of this expression. + */ + public ArrayRefExpr(Expr array, Expr index, Type elementType, Type type) { + super(type); + this.array = array; + this.index = index; + this.elementType = elementType; + + array.setParent(this); + index.setParent(this); + } + + public Expr array() { + return array; + } + + public Expr index() { + return index; + } + + public Type elementType() { + return elementType; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + index.visit(visitor); + array.visit(visitor); + } else { + array.visit(visitor); + index.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitArrayRefExpr(this); + } + + public int exprHashCode() { + return 4 + array.exprHashCode() ^ index.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof ArrayRefExpr + && ((ArrayRefExpr) other).array.equalsExpr(array) + && ((ArrayRefExpr) other).index.equalsExpr(index); + } + + public Object clone() { + return copyInto(new ArrayRefExpr((Expr) array.clone(), (Expr) index + .clone(), elementType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/AscendVisitor.java b/src/edu/purdue/cs/bloat/tree/AscendVisitor.java new file mode 100644 index 0000000..f5547bd --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/AscendVisitor.java @@ -0,0 +1,454 @@ +/* + * Class: AscendVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * AscendVisitor is the superclass of Type0Visitor and Type1Visitor, + * conveniently containing the common code. It makes an upward traversal of the + * tree as if it were a binary tree (nodes with more than two children, such as + * a method call, are considered in a form similar to curried form). + * + * @author Thomas VanDrunen + */ + +public abstract class AscendVisitor extends TreeVisitor { + + HashMap defInfoMap; /* the same as the fields of Stack Optimizer */ + + HashMap useInfoMap; /* of the same name */ + + LocalExpr start; /* where we start the search from */ + + Node previous; + + Vector visited; + + public AscendVisitor(HashMap defInfoMap, HashMap useInfoMap) { + this.defInfoMap = defInfoMap; + this.useInfoMap = useInfoMap; + + visited = new Vector(); + } + + abstract public void check(Node node); + + public void visitTree(Tree tree) { + + ListIterator iter = tree.stmts().listIterator( + tree.stmts().lastIndexOf(previous)); + + if (iter.hasPrevious()) { + Stmt p = (Stmt) iter.previous(); + check(p); + } + /* + * Object prev = iter.previous(); if (prev instanceof LocalExpr) + * check(prev); + */ + } + + public void visitExprStmt(ExprStmt stmt) { + + previous = stmt; + stmt.parent().visit(this); + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + + if (stmt.right() == previous) + check(stmt.left()); + else if (stmt.left() == previous) { + previous = stmt; + stmt.parent().visit(this); + } + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + + previous = stmt; + stmt.parent.visit(this); + } + + public void visitInitStmt(InitStmt stmt) { + + LocalExpr[] targets = stmt.targets(); + for (int i = 0; i < targets.length; i++) + if (targets[i] == previous) + if (i > 0) + check(targets[i - 1]); + else + break; + } + + public void visitGotoStmt(GotoStmt stmt) { + + } + + public void visitLabelStmt(LabelStmt stmt) { + + } + + public void visitMonitorStmt(MonitorStmt stmt) { + + previous = stmt; + stmt.parent().visit(this); + } + + public void visitPhiStmt(PhiStmt stmt) { + + if (stmt instanceof PhiCatchStmt) + visitPhiCatchStmt((PhiCatchStmt) stmt); + else if (stmt instanceof PhiJoinStmt) + visitPhiJoinStmt((PhiJoinStmt) stmt); + /* + * else if (stmt instanceof PhiReturnStmt) + * visitPhiReturnStmt((PhiReturnStmt) stmt); + */ + } + + public void visitCatchExpr(CatchExpr expr) { + + } + + public void visitDefExpr(DefExpr expr) { + if (expr instanceof MemExpr) + visitMemExpr((MemExpr) expr); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + + } + + public void visitRetStmt(RetStmt stmt) { + + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + + previous = stmt; + stmt.parent.visit(this); + } + + public void visitReturnStmt(ReturnStmt stmt) { + + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + + } + + public void visitStoreExpr(StoreExpr expr) { + + if (expr.target() instanceof LocalExpr + || expr.target() instanceof StaticFieldExpr) { + if (previous == expr.expr()) { // can't be target, because then + // it would be a definition, for which + // Type0Visitor is not called + previous = expr; + expr.parent.visit(this); + } + } + + else if (expr.target() instanceof ArrayRefExpr) { + if (previous == expr.expr()) + check(((ArrayRefExpr) expr.target()).index()); + else if (previous == expr.target()) { + previous = expr; + expr.parent.visit(this); + } + } + + else if (expr.target() instanceof FieldExpr) { + if (previous == expr.expr()) + check(expr.target()); + else if (previous == expr.target()) { + previous = expr; + expr.parent.visit(this); + } + } + } + + public void visitJsrStmt(JsrStmt stmt) { + + } + + public void visitSwitchStmt(SwitchStmt stmt) { + + if (previous == stmt.index()) { + previous = stmt; + stmt.parent.visit(this); + } + } + + public void visitThrowStmt(ThrowStmt stmt) { + + } + + public void visitStmt(Stmt stmt) { + + } + + public void visitSCStmt(SCStmt stmt) { + + } + + public void visitSRStmt(SRStmt stmt) { + + } + + public void visitArithExpr(ArithExpr expr) { + + if (previous == expr.left()) { + previous = expr; + expr.parent.visit(this); + } else if (previous == expr.right()) + check(expr.left()); + + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + + } + + public void visitMemExpr(MemExpr expr) { + + if (expr instanceof MemRefExpr) + visitMemRefExpr((MemRefExpr) expr); + else if (expr instanceof VarExpr) + visitVarExpr((VarExpr) expr); + + } + + public void visitMemRefExpr(MemRefExpr expr) { + if (expr instanceof FieldExpr) + visitFieldExpr((FieldExpr) expr); + else if (expr instanceof StaticFieldExpr) + visitStaticFieldExpr((StaticFieldExpr) expr); + else if (expr instanceof ArrayRefExpr) + visitArrayRefExpr((ArrayRefExpr) expr); + + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + + if (previous == expr.array()) { // the array reference is like the + previous = expr; // left child + expr.parent().visit(this); + } else if (previous == expr.index()) // the index is like the + check(expr.array()); // right child + } + + public void visitCallExpr(CallExpr expr) { + if (expr instanceof CallMethodExpr) + visitCallMethodExpr((CallMethodExpr) expr); + if (expr instanceof CallStaticExpr) + visitCallStaticExpr((CallStaticExpr) expr); + + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + + if (previous == expr.receiver()) { + previous = expr; + expr.parent.visit(this); + } + + else { + Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) + if (params[i] == previous) + if (i > 0) + check(params[i - 1]); + else + check(expr.receiver()); + } + + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + + Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) + if (params[i] == previous) { + if (i > 0) + check(params[i - 1]); + else { + previous = expr; + expr.parent().visit(this); + } + break; + } + + } + + public void visitCastExpr(CastExpr expr) { + + previous = expr; + expr.parent.visit(this); + } + + public void visitConstantExpr(ConstantExpr expr) { + + } + + public void visitFieldExpr(FieldExpr expr) { + + if (previous == expr.object()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + + if (previous == expr.expr()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitLocalExpr(LocalExpr expr) { + + } + + public void visitNegExpr(NegExpr expr) { + + if (previous == expr.expr()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + + if (previous == expr.size()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitNewExpr(NewExpr expr) { + + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + + Expr[] dims = expr.dimensions; + for (int i = 0; i < dims.length; i++) + if (dims[i] == previous) + if (i > 0) + check(dims[i - 1]); + else { + previous = expr; + expr.parent().visit(this); + } + + } + + public void visitCheckExpr(CheckExpr expr) { + if (expr instanceof ZeroCheckExpr) + visitZeroCheckExpr((ZeroCheckExpr) expr); + else if (expr instanceof RCExpr) + visitRCExpr((RCExpr) expr); + else if (expr instanceof UCExpr) + visitUCExpr((UCExpr) expr); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + /* + * if (previous == expr.expr()) { previous = expr; + * expr.parent.visit(this); } + */ + } + + public void visitRCExpr(RCExpr expr) { + + } + + public void visitUCExpr(UCExpr expr) { + + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + + } + + public void visitShiftExpr(ShiftExpr expr) { + + if (previous == expr.expr()) { // the expression to be shifted is like + previous = expr; // the left child + expr.parent().visit(this); + } else if (previous == expr.bits()) // the bits shifted is like + check(expr.expr()); // the right child + } + + public void visitStackExpr(StackExpr expr) { + + } + + public void visitVarExpr(VarExpr expr) { + if (expr instanceof LocalExpr) + visitLocalExpr((LocalExpr) expr); + if (expr instanceof StackExpr) + visitStackExpr((StackExpr) expr); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + + } + + public void visitExpr(Expr expr) { + + } + + public void visitNode(Node node) { + + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Assign.java b/src/edu/purdue/cs/bloat/tree/Assign.java new file mode 100644 index 0000000..463ce2f --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Assign.java @@ -0,0 +1,56 @@ +/* + * Class: Assign + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * Classes that implement Assign involve an assignment (definition). + * + * @see InitStmt + * @see PhiStmt + * @see StackManipStmt + * @see StoreExpr + */ +public interface Assign { + /** + * Returns the expressions that may be modified (defined) by this expression + * or statement. + */ + public abstract DefExpr[] defs(); +} diff --git a/src/edu/purdue/cs/bloat/tree/CallExpr.java b/src/edu/purdue/cs/bloat/tree/CallExpr.java new file mode 100644 index 0000000..99c2ccd --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CallExpr.java @@ -0,0 +1,89 @@ +/* + * Class: CallExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.editor.MethodRef; + +/** + * CallExpr is a superclass of expressions that represent the + * invocation of a method. It consists of an array of Expr that + * represent the arguments to a method and a MethodRef that + * represents the method itself. + * + * @see CallMethodExpr + * @see CallStaticExpr + */ +public abstract class CallExpr extends Expr { + final Expr[] params; // The parameters to the method + + final MethodRef method; // The method to be invoked + + public int voltaPos; // used for placing swaps and stuff + + /** + * Constructor. + * + * @param params + * Parameters to the method. Note that these parameters do not + * contain parameter 0, the "this" pointer. + * @param method + * The method that is to be invoked. + * @param type + * The type of this expression (i.e. the return type of the + * method being called). + */ + public CallExpr(Expr[] params, MethodRef method) { + super(method.type()); + this.params = params; + this.method = method; + + for (int i = 0; i < params.length; i++) { + params[i].setParent(this); + } + } + + public MethodRef method() { + return method; + } + + public Expr[] params() { + return params; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CallMethodExpr.java b/src/edu/purdue/cs/bloat/tree/CallMethodExpr.java new file mode 100644 index 0000000..08642a1 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CallMethodExpr.java @@ -0,0 +1,141 @@ +/* + * Class: CallMethodExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.editor.MethodRef; + +/** + * CallMethodExpr represents the invocation of an object's method. In addition + * to knowing what method is being called and its parameters, it also knows what + * "kind" of method call it is (VIRTUAL, NONVIRTUAL, or + * INTERFACE) and the object that is the reciever of this method + * call. + * + * @see CallStaticExpr + */ +public class CallMethodExpr extends CallExpr { + // Different kinds of methods to call... + public static final int VIRTUAL = 0; // invokevirtual + + public static final int NONVIRTUAL = 1; // invokespecial + + public static final int INTERFACE = 2; // invokeinterface + + Expr receiver; + + final int kind; + + /** + * Constructor. + * + * @param kind + * The kind (VIRTUAL, NONVIRTUAL, or INTERFACE) of method that is + * being called. + * @param receiver + * The expression (object) whose method is being called. + * @param params + * Parameters to the method. + * @param method + * The method being called. + * @param type + * The type of this expression. + */ + public CallMethodExpr(int kind, Expr receiver, Expr[] params, + MethodRef method) { + super(params, method); + this.receiver = receiver; + this.kind = kind; + + receiver.setParent(this); + } + + public int kind() { + return kind; + } + + public Expr receiver() { + return receiver; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + for (int i = params.length - 1; i >= 0; i--) { + params[i].visit(visitor); + } + + receiver.visit(visitor); + } else { + receiver.visit(visitor); + + for (int i = 0; i < params.length; i++) { + params[i].visit(visitor); + } + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitCallMethodExpr(this); + } + + public int exprHashCode() { + int v = 5 + kind ^ receiver.exprHashCode(); + + for (int i = 0; i < params.length; i++) { + v ^= params[i].exprHashCode(); + } + + return v; + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + Expr[] p = new Expr[params.length]; + + for (int i = 0; i < params.length; i++) { + p[i] = (Expr) params[i].clone(); + } + + return copyInto(new CallMethodExpr(kind, (Expr) receiver.clone(), p, + method)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CallStaticExpr.java b/src/edu/purdue/cs/bloat/tree/CallStaticExpr.java new file mode 100644 index 0000000..d8965cf --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CallStaticExpr.java @@ -0,0 +1,106 @@ +/* + * Class: CallStaticExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.editor.MethodRef; + +/** + * CallStaticExpr represents the invokestatic opcode which invokes a + * class (static) method. Static methods can always be inlined. + * + * @see CallMethodExpr + */ +public class CallStaticExpr extends CallExpr { + // invokestatic + + /** + * Constructor. + * + * @param params + * Parameters to the method. + * @param method + * The (class) method to be invoked. + * @param type + * The type of this expression. + */ + public CallStaticExpr(Expr[] params, MethodRef method) { + super(params, method); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + for (int i = params.length - 1; i >= 0; i--) { + params[i].visit(visitor); + } + } else { + for (int i = 0; i < params.length; i++) { + params[i].visit(visitor); + } + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitCallStaticExpr(this); + } + + public int exprHashCode() { + int v = 6; + + for (int i = 0; i < params.length; i++) { + v ^= params[i].exprHashCode(); + } + + return v; + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + Expr[] p = new Expr[params.length]; + + for (int i = 0; i < params.length; i++) { + p[i] = (Expr) params[i].clone(); + } + + return copyInto(new CallStaticExpr(p, method)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CastExpr.java b/src/edu/purdue/cs/bloat/tree/CastExpr.java new file mode 100644 index 0000000..11830c9 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CastExpr.java @@ -0,0 +1,116 @@ +/* + * Class: CastExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * CastExpr represents an expression that casts an object to a given type. + */ +public class CastExpr extends Expr { + Expr expr; // An expression (object) to cast + + final Type castType; // The Type to cast expr to + + /** + * Constructor. + * + * @param expr + * Expression (object) to be cast. + * @param type + * The type to which to cast expr and as well as the type of this + * expression. + */ + public CastExpr(Expr expr, Type type) { + this(expr, type, type); + } + + /** + * Constructor. + * + * @param expr + * Expression (object) to be cast. + * @param castType + * The type to which to cast expr. + * @param type + * The type of this expression. + */ + public CastExpr(Expr expr, Type castType, Type type) { + super(type); + this.expr = expr; + this.castType = castType; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public Type castType() { + return castType; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitCastExpr(this); + } + + public int exprHashCode() { + return 7 + expr.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof CastExpr + && ((CastExpr) other).castType.equals(castType) + && ((CastExpr) other).expr.equalsExpr(expr); + } + + public Object clone() { + return copyInto(new CastExpr((Expr) expr.clone(), castType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CatchExpr.java b/src/edu/purdue/cs/bloat/tree/CatchExpr.java new file mode 100644 index 0000000..2bbf731 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CatchExpr.java @@ -0,0 +1,104 @@ +/* + * Class: CatchExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.util.Assert; + +/** + * CatchExpr represents an expression that catches an exception. A CatchExpr is + * used when evaluating a method's try-catch blocks when a control flow graph is + * constructed. + * + * @see TryCatch + * @see FlowGraph#FlowGraph + * @see MethodEditor + */ +public class CatchExpr extends Expr { + final Type catchType; + + /** + * Constructor. + * + * @param catchType + * The type of the exception that is being caught. + * @param type + * The type of this expression. + */ + public CatchExpr(Type catchType, Type type) { + super(type); + this.catchType = catchType; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitCatchExpr(this); + } + + public Type catchType() { + return catchType; + } + + public int exprHashCode() { + return 8 + (int) Assert.simple(type).hashCode() ^ catchType.hashCode(); + } + + public boolean equalsExpr(Expr other) { + if (other instanceof CatchExpr) { + CatchExpr c = (CatchExpr) other; + + if (catchType != null) { + return catchType.equals(c.catchType); + } else { + return c.catchType == null; + } + } + + return false; + } + + public Object clone() { + return copyInto(new CatchExpr(catchType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CheckExpr.java b/src/edu/purdue/cs/bloat/tree/CheckExpr.java new file mode 100644 index 0000000..d2eaa37 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CheckExpr.java @@ -0,0 +1,96 @@ +/* + * Class: CheckExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * CheckExpr is a superclass for classes representing a check on an expression. + * For instance, a CheckExpr is inserted into the tree before the divisor of a + * divide operation. The CheckExpr checks to make sure that the divisor is not + * zero. + * + * @see RCExpr + * @see UCExpr + * @see ZeroCheckExpr + */ +public abstract class CheckExpr extends Expr { + Expr expr; + + /** + * Constructor. + * + * @param expr + * An expression that is to be checked. + * @param type + * The type of this expression. + */ + public CheckExpr(Expr expr, Type type) { + super(type); + this.expr = expr; + expr.setParent(this); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + /** + * Returns the expression being checked. + */ + public Expr expr() { + return expr; + } + + public int exprHashCode() { + return 9 + expr.exprHashCode() ^ (int) Assert.simple(type).hashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof CheckExpr + && ((CheckExpr) other).expr.equalsExpr(expr); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/CondExpr.java b/src/edu/purdue/cs/bloat/tree/CondExpr.java new file mode 100644 index 0000000..131392d --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/CondExpr.java @@ -0,0 +1,60 @@ +/* + * Class: CondExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * CondExpr is a superclass for conditional expressions. That is, an expression + * that yields a true or false value. + * + * @see InstanceOfExpr + */ +public abstract class CondExpr extends Expr { + /** + * Constructor. + * + * @param type + * The Type of this expression. + */ + public CondExpr(Type type) { + super(type); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ConstantExpr.java b/src/edu/purdue/cs/bloat/tree/ConstantExpr.java new file mode 100644 index 0000000..8b5e3a8 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ConstantExpr.java @@ -0,0 +1,127 @@ +/* + * Class: ConstantExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ConstantExpr represents a constant expression. It is used when opcodes ldc, + * iinc, and getstatic are visited. It value must be an Integer, + * Long, Float, Double, or String. + */ +public class ConstantExpr extends Expr implements LeafExpr { + // ldc + + final Object value; // The operand to the ldc instruction + + /** + * Constructor. + * + * @param value + * The operand of the ldc instruction + * @param type + * The Type of the operand + */ + public ConstantExpr(Object value, Type type) { + super(type); + this.value = value; + /* + * Assert.isTrue(((value == null)||(value instanceof Integer)||(value + * instanceof Long)|| (value instanceof Float)||(value instanceof + * Double)|| (value instanceof String)||(value instanceof Byte)|| (value + * instanceof Short)||(value instanceof Boolean)), "Illegal ConstantExpr + * value:" + value); + */ + } + + /** + * @return The operand of the ldc instruction + */ + public Object value() { + return value; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitConstantExpr(this); + } + + /** + * @return A hash code for this expression. + */ + public int exprHashCode() { + if (value != null) { + return 10 + value.hashCode(); + } + + return 10; + } + + /** + * Compare this ConstantExpr to another Expr. + * + * @param other + * An Expr to compare this to. + * + * @return True, if this and other are the same (that is, have the same + * contents). + */ + public boolean equalsExpr(Expr other) { + if (!(other instanceof ConstantExpr)) { + return false; + } + + if (value == null) { + return ((ConstantExpr) other).value == null; + } + + if (((ConstantExpr) other).value == null) { + return false; + } + + return ((ConstantExpr) other).value.equals(value); + } + + public Object clone() { + return copyInto(new ConstantExpr(value, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/DefExpr.java b/src/edu/purdue/cs/bloat/tree/DefExpr.java new file mode 100644 index 0000000..1db0612 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/DefExpr.java @@ -0,0 +1,136 @@ +/* + * Class: DefExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; +import org.apache.bcel.generic.Type; + +/** + * An expression in which a definition occurs. Each instance has a unique + * version number associated with it. + */ +public abstract class DefExpr extends Expr { + Set uses; // Expressions in which the definition is used + + int version; // Which number DefExpr is this? + + static int next = 0; // Total number of DefExprs + + /** + * Constructor. + * + * @param type + * The Type (descriptor) of this expression + */ + public DefExpr(Type type) { + super(type); + uses = new LinkedHashSet(); + version = next++; + } + + /** + * Clean up this expression. Notify all the expressions that use this + * definition that it is no longer their defining expression. + */ + public void cleanupOnly() { + super.cleanupOnly(); + + List a = new ArrayList(uses); + + uses.clear(); + + Iterator e = a.iterator(); + + while (e.hasNext()) { + Expr use = (Expr) e.next(); + use.setDef(null); + } + } + + /** + * Returns Number DefExpr this is. This is also the SSA version number of + * the expression that this DefExpr defines. + */ + public int version() { + return version; + } + + /** + * Determines whether or not this DefExpr defines a local + * variable in its parent. + * + * @see Assign#defs + */ + public boolean isDef() { + if (parent instanceof Assign) { + DefExpr[] defs = ((Assign) parent).defs(); + + if (defs != null) { + for (int i = 0; i < defs.length; i++) { + if (defs[i] == this) { + return true; + } + } + } + } + + return false; + } + + /** + * Returns the Exprs in which the variable defined by this are + * used. + */ + public Collection uses() { + return new LinkedHashSet(uses); + } + + public boolean hasUse(Expr use) { + return uses.contains(use); + } + + protected void addUse(Expr use) { + uses.add(use); + } + + protected void removeUse(Expr use) { + uses.remove(use); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/DefInformation.java b/src/edu/purdue/cs/bloat/tree/DefInformation.java new file mode 100644 index 0000000..e715116 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/DefInformation.java @@ -0,0 +1,60 @@ +/* + * Class: DefInformation + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * DefInformation contains information about the definition of a local variable + * + * @author Thomas VanDrunen + */ +public class DefInformation { + + int type1s; + + int uses; + + int usesFound; + + public DefInformation(int uses) { + type1s = 0; + this.uses = uses; + usesFound = 0; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/DescendVisitor.java b/src/edu/purdue/cs/bloat/tree/DescendVisitor.java new file mode 100644 index 0000000..7519607 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/DescendVisitor.java @@ -0,0 +1,452 @@ +/* + * Class: DescendVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * DecsendVisitor is the superclass of a few private classes of Type0Visitor and + * Type1Visitor. It descends the tree, keeping track of the number of right + * links that have been taken. + */ +public abstract class DescendVisitor extends TreeVisitor { + + HashMap useInfoMap; + + HashMap defInfoMap; + + boolean found; + + Node beginNode; // where this visitor starts its search from + + LocalExpr start; // where the original search began + + int exchangeFactor; + + public DescendVisitor(HashMap useInfoMap, HashMap defInfoMap) { + this.useInfoMap = useInfoMap; + this.defInfoMap = defInfoMap; + } + + public boolean search(Node beginNode, LocalExpr start) { + this.beginNode = beginNode; + this.start = start; + exchangeFactor = 0; + found = false; + + beginNode.visit(this); + + return found; + } + + public void visitExprStmt(ExprStmt stmt) { + stmt.expr().visit(this); + } + + public void visitIfStmt(IfStmt stmt) { + + if (stmt instanceof IfCmpStmt) + visitIfCmpStmt((IfCmpStmt) stmt); + else if (stmt instanceof IfZeroStmt) + visitIfZeroStmt((IfZeroStmt) stmt); + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + stmt.left().visit(this); // search the left branch + if (!found) { // if nothing has been found + exchangeFactor++; // increase the exchange factor, + if (stmt.left().type().getSize() == 2) + exchangeFactor++; // twice to get + // around wides + if (exchangeFactor < 3) + stmt.right().visit(this); // search the right branch. + } + + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + + stmt.expr().visit(this); + + } + + public void visitInitStmt(InitStmt stmt) { + + // would have been checked by the Type0Visitor + + } + + public void visitGotoStmt(GotoStmt stmt) { + + } + + public void visitLabelStmt(LabelStmt stmt) { + + } + + public void visitMonitorStmt(MonitorStmt stmt) { + + } + + public void visitPhiStmt(PhiStmt stmt) { + if (stmt instanceof PhiCatchStmt) + visitPhiCatchStmt((PhiCatchStmt) stmt); + else if (stmt instanceof PhiJoinStmt) + visitPhiJoinStmt((PhiJoinStmt) stmt); + /* + * else if (stmt instanceof PhiReturnStmt) + * visitPhiReturnStmt((PhiReturnStmt) stmt); + */ + } + + public void visitCatchExpr(CatchExpr expr) { + + } + + public void visitDefExpr(DefExpr expr) { + if (expr instanceof MemExpr) + visitMemExpr((MemExpr) expr); + + } + + public void visitStackManipStmt(StackManipStmt stmt) { + + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + + } + + public void visitRetStmt(RetStmt stmt) { + + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + + } + + public void visitReturnStmt(ReturnStmt stmt) { + + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + + } + + // StoreExprs are very difficult because they represent several + // types of expressions. What we do will depend on what the target + // of the store is: ArrayRefExpr, FieldExpr, StaticFieldExpr, + // or LocalExpr + + public void visitStoreExpr(StoreExpr expr) { + MemExpr target = expr.target(); + + if (target instanceof ArrayRefExpr) { + // ArrayRefExpr: the store will be something like an astore + // which manipulates the stack like + // arrayref, index, val => ... + // so, think of the tree like + // (StoreExpr) + // / \ + // Array Ref . + // / \ + // index value + // This is unlike the structure of the tree BLOAT uses for + // intermediate representation, but it better relates to what's + // on the stack at what time + + ((ArrayRefExpr) target).array().visit(this); // visit the + // left child + if (!found) { // if match wasn't found + exchangeFactor++; // take the right branch + if (exchangeFactor < 3) { // (an array ref isn't wide) + // visit next left child + ((ArrayRefExpr) target).index().visit(this); + if (!found) { // if match wasn't found + exchangeFactor++; + if (exchangeFactor < 3) // (an index isn't wide) + expr.expr().visit(this); // search the right + // branch + } // end seaching RR + } + } // end searching R + } // end case where target is ArrayRefExpr + + else if (target instanceof FieldExpr) { + // FieldExpr: the store will be like a putfield + // which manipulates the stack like + // objref, val => ... + // so, think of the tree like + // (StoreExpr) + // / \ + // Object Ref value + + ((FieldExpr) target).object().visit(this); // visit the left child + + if (!found) { + exchangeFactor++; // (an object ref isn't wide) + if (exchangeFactor < 3) { + expr.expr().visit(this); + } + } + } // end case where target is FieldRef + + else if (target instanceof StaticFieldExpr) { + // StaticFieldExpr: the store will be like a putstatic + // which manipulates the stack like + // val => ... + // so, think of the tree like + // (StoreExpr) + // / + // value + + expr.expr.visit(this); + } + + else if (target instanceof LocalExpr) { + // LocalExpr: the store will be like istore/astore/etc. + // which manipulates the stack like + // val => ... + // so, think of the tree like + // (StoreExpr) + // / + // value + + expr.expr.visit(this); + } + } + + public void visitJsrStmt(JsrStmt stmt) { + + } + + public void visitSwitchStmt(SwitchStmt stmt) { + + } + + public void visitThrowStmt(ThrowStmt stmt) { + + } + + public void visitStmt(Stmt stmt) { + + } + + public void visitSCStmt(SCStmt stmt) { + + } + + public void visitSRStmt(SRStmt stmt) { + + } + + public void visitArithExpr(ArithExpr expr) { // important one + expr.left().visit(this); // visit the left branch + + if (!found) { // if a match isn't found yet + exchangeFactor++; // increase the exchange factor + if (expr.left().type().getSize() == 2) + exchangeFactor++; + // twice if wide + if (exchangeFactor < 3) { + expr.right().visit(this); // visit right branch + } + } + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + expr.array().visit(this); + } + + public void visitMemExpr(MemExpr expr) { + if (expr instanceof LocalExpr) + visitLocalExpr((LocalExpr) expr); + } + + public void visitMemRefExpr(MemRefExpr expr) { + + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + + } + + public void visitCallExpr(CallExpr expr) { + if (expr instanceof CallMethodExpr) + visitCallMethodExpr((CallMethodExpr) expr); + else if (expr instanceof CallStaticExpr) + visitCallStaticExpr((CallStaticExpr) expr); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + // Method invocations are to be thought of, in terms of + // binary expression trees, as + // (CallMethodExpr) + // / \ + // receiver . + // / \ + // arg1 . + // / \ + // arg2 . + // / \ + // arg3 ... + // This might be the opposite of what one would think in terms + // of currying (ie, one might think of currying in terms of + // left associativity), but this gives a better picture of what + // happens to the stack when invokestatic or invokevirtual is called: + // objectref, [arg1, [arg2 ...]] => ... + + expr.receiver().visit(this); + Expr[] params = expr.params(); + if (!found && exchangeFactor < 2 && params.length > 0) { + exchangeFactor++; // (reciever won't be wide) + params[0].visit(this); + } + + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + + Expr[] params = expr.params(); + if (params.length > 0) + params[0].visit(this); + if (!found && exchangeFactor < 2 && params.length > 1) { + exchangeFactor++; + params[1].visit(this); + } + } + + public void visitCastExpr(CastExpr expr) { + expr.expr().visit(this); + } + + public void visitConstantExpr(ConstantExpr expr) { + + } + + public void visitFieldExpr(FieldExpr expr) { + expr.object.visit(this); + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + expr.expr().visit(this); + } + + /* needs to be different for Type0 and Type1 */ + public abstract void visitLocalExpr(LocalExpr expr); + + public void visitNegExpr(NegExpr expr) { + expr.expr().visit(this); + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + expr.size().visit(this); + } + + public void visitNewExpr(NewExpr expr) { + + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + // Think of the tree like + // (NewMultiArrayExpr) + // / \ + // count1 . + // / \ + // count2 etc. + // since multianewarray manipulates the stack like + // count1, [count1 ...] => ... + + Expr[] dims = expr.dimensions(); + if (dims.length > 0) + dims[0].visit(this); + if (!found && exchangeFactor < 2 && dims.length > 1) { + exchangeFactor++; // (count1 won't be wide) + dims[1].visit(this); + } + } + + public void visitCheckExpr(CheckExpr expr) { + if (expr instanceof ZeroCheckExpr) + visitZeroCheckExpr((ZeroCheckExpr) expr); + else if (expr instanceof RCExpr) + visitRCExpr((RCExpr) expr); + else if (expr instanceof UCExpr) + visitUCExpr((UCExpr) expr); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + // perhaps add something here + } + + public void visitRCExpr(RCExpr expr) { + + } + + public void visitUCExpr(UCExpr expr) { + + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + + } + + public void visitShiftExpr(ShiftExpr expr) { + + } + + public void visitVarExpr(VarExpr expr) { + if (expr instanceof LocalExpr) + visitLocalExpr((LocalExpr) expr); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + + } + + public void visitExpr(Expr expr) { + + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/EliminationInformation.java b/src/edu/purdue/cs/bloat/tree/EliminationInformation.java new file mode 100644 index 0000000..eed9d8b --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/EliminationInformation.java @@ -0,0 +1,65 @@ +/* + * Class: EliminationInformation + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author John N Zigman + * @author Arrin Daley + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +public class EliminationInformation { + + public Vector Occurences; + + public int type1s; + + public int uniqueNumber; + + static int nextUN = 0; + + public EliminationInformation() { + uniqueNumber = nextUN; + nextUN++; + Occurences = new Vector(); + type1s = 0; + } + + public String toString() { + return uniqueNumber + "+" + type1s + Occurences.toString(); + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/Expr.java b/src/edu/purdue/cs/bloat/tree/Expr.java new file mode 100644 index 0000000..8057ceb --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Expr.java @@ -0,0 +1,246 @@ +/* + * Class: Expr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * Expr is the superclass for a number of other classes representing expressions + * in byte code. Expressions are typed and may be nested. + * + * @see DefExpr + */ +public abstract class Expr extends Node implements Cloneable { + protected Type type; // The type (descriptor) of this expression + + private DefExpr def; // The expression in which this expression + + // is defined (if applicable) + private Object comparator; + + /** + * Constructor. Initializes an expression with a given type. + * + * @param type + * The initial Type (descriptor) of this expression. + */ + public Expr(Type type) { + this.def = null; + this.comparator = new ExprComparator(); + this.type = type; + } + + /** + * Sets the type of this expression. Returns whether or not the type changed + * as a result of calling this method. + */ + public boolean setType(Type type) { + + if (!this.type.equals(type)) { + // Assert.isTrue((type.equals(Type.OBJECT) || + // type.equals(Type.INT) || + // type.equals(Type.LONG) || type.equals(Type.FLOAT) || + // type.equals(Type.DOUBLE) || type.equals(Type.STRING) || + // type.equals(Type.BYTE) || type.equals(Type.SHORT) || + // type.equals(Type.BOOLEAN)), + // "Illegal ConstantExpr type:" + type); + + // if (Tree.DEBUG) { + // System.out.println(" setting typeof(" + this + ") = " + + // type); + // } + this.type = type; + + return true; + } + + return false; + } + + /** + * Returns whether or not this expression is a defining occurrence. By + * default, false is returned. + */ + public boolean isDef() { + return false; + } + + /** + * Returns the statement to which this expression belongs. It essentially + * searches up the expression tree for this expression's first ancestor + * which is a Stmt. + */ + public Stmt stmt() { + Node p = parent; + + while (!(p instanceof Stmt)) { + Assert.isTrue(!(p instanceof Tree), "Invalid ancestor of " + this); + Assert.isTrue(p != null, "Null ancestor of " + this); + p = p.parent; + } + + return (Stmt) p; + } + + /** + * Returns the Type of this expression. + */ + public Type type() { + return type; + } + + /** + * Cleans up this expression only, not its children. + */ + public void cleanupOnly() { + setDef(null); + } + + /** + * Sets the expression that defines this expression. + * + * @param def + * Defining expression. + */ + public void setDef(DefExpr def) { + // if (Tree.DEBUG) { + // System.out.println(" setting def of " + this + + // " (" + System.identityHashCode(this) + ") to " + def); + // } + + if (this.def == def) { + return; + } + + // If this Expr already had a defining statement, remove this from the + // DefExpr use list. + if (this.def != null) { + this.def.removeUse(this); + } + + if (this.isDef()) { + Assert.isTrue(def == this || def == null); + this.def = null; + return; + } + + this.def = def; + + if (this.def != null) { + this.def.addUse(this); // This Expr is a use of def + } + } + + /** + * Returns the expression in which this Expr is defined. + */ + public DefExpr def() { + return def; + } + + /** + * Returns the hash code for this expresion. + */ + public abstract int exprHashCode(); + + /** + * Compares this expression to another. + * + * @param other + * Expr to which to compare this. + */ + public abstract boolean equalsExpr(Expr other); + + public abstract Object clone(); + + /** + * Copies the contents of another expression in this one. + * + * @param expr + * The expression from which to copy. + */ + protected Expr copyInto(Expr expr) { + expr = (Expr) super.copyInto(expr); + + DefExpr def = def(); + + if (isDef()) { + expr.setDef(null); + } else { + expr.setDef(def); + } + + return expr; + } + + /** + * Returns an Object that can be used to compare other Expr to this. + */ + public Object comparator() { + return comparator; + } + + /** + * ExprComparator is used to provide a different notion of equality among + * expressions than the default ==. In most cases, we want ==, but + * occasionally we want the equalsExpr() functionality when inserting in + * Hashtables, etc. + */ + private class ExprComparator { + Expr expr = Expr.this; + + public boolean equals(Object obj) { + if (obj instanceof ExprComparator) { + Expr other = ((ExprComparator) obj).expr; + return expr.equalsExpr(other) + && Assert.simple(expr.type).equals( + Assert.simple(other.type)); + } + + return false; + } + + public int hashCode() { + return Expr.this.exprHashCode(); + } + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ExprStmt.java b/src/edu/purdue/cs/bloat/tree/ExprStmt.java new file mode 100644 index 0000000..56d73ec --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ExprStmt.java @@ -0,0 +1,76 @@ +/* + * Class: ExprStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * ExprStmt is a statement consisting of an expression. + * + * @see Expr + */ +public class ExprStmt extends Stmt { + Expr expr; // Expression contained in this statement + + /** + * Constructor. + * + * @param expr + * The expression contained in this statement. + */ + public ExprStmt(Expr expr) { + this.expr = expr; + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitExprStmt(this); + } + + public Object clone() { + return copyInto(new ExprStmt((Expr) expr.clone())); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/FieldExpr.java b/src/edu/purdue/cs/bloat/tree/FieldExpr.java new file mode 100644 index 0000000..f5578d9 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/FieldExpr.java @@ -0,0 +1,109 @@ +/* + * Class: FieldExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.util.Assert; + +/** + * FieldExpr represents the getfield opcode which fetches a field from + * an object. + * + * @see MemberRef + */ +public class FieldExpr extends MemRefExpr { + Expr object; // The object whose field we are fetching + + final MemberRef field; // The field to fetch + + /** + * Constructor. + * + * @param object + * The object (result of an Expr) whose field is to be fetched. + * @param field + * The field of object to be fetched. + * @param type + * The type of this expression. + */ + public FieldExpr(Expr object, MemberRef field, Type type) { + super(type); + this.object = object; + this.field = field; + + object.setParent(this); + } + + public Expr object() { + return object; + } + + public MemberRef field() { + return field; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + object.visit(visitor); + } else { + object.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitFieldExpr(this); + } + + public int exprHashCode() { + return 11 + object.exprHashCode() ^ Assert.simple(type).hashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof FieldExpr + && ((FieldExpr) other).field.equals(field) + && ((FieldExpr) other).object.equalsExpr(object); + } + + public Object clone() { + return copyInto(new FieldExpr((Expr) object.clone(), field, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/GotoStmt.java b/src/edu/purdue/cs/bloat/tree/GotoStmt.java new file mode 100644 index 0000000..637f60b --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/GotoStmt.java @@ -0,0 +1,78 @@ +/* + * Class: GotoStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * Represents an unconditional branch to a basic block. + */ +public class GotoStmt extends JumpStmt { + Block target; // The basic Block that is the target of this goto + + /** + * Constructor. + * + * @param target + * The basic Block that is the target of this goto statement. + */ + public GotoStmt(Block target) { + this.target = target; + } + + public void setTarget(Block target) { + this.target = target; + } + + public Block target() { + return target; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitGotoStmt(this); + } + + public Object clone() { + return copyInto(new GotoStmt(target)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/IfCmpStmt.java b/src/edu/purdue/cs/bloat/tree/IfCmpStmt.java new file mode 100644 index 0000000..61ea9cc --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/IfCmpStmt.java @@ -0,0 +1,102 @@ +/* + * Class: IfCmpStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * IfCmpStmt consists of a comparison expression (a left-hand expression, a + * comparison operator, and a right-hand expression) that is to be evaluated. + */ +public class IfCmpStmt extends IfStmt { + Expr left; + + Expr right; + + /** + * Constructor. + * + * @param comparison + * Comparison operator for this if statement. + * @param left + * Expression on the left side of the comparison. + * @param right + * Expression on the right side of the comparison. + * @param trueTarget + * Block executed if comparison evaluates to true. + * @param falseTarget + * Block executed if comparison evaluates to false. + */ + public IfCmpStmt(int comparison, Expr left, Expr right, Block trueTarget, + Block falseTarget) { + super(comparison, trueTarget, falseTarget); + this.left = left; + this.right = right; + left.setParent(this); + right.setParent(this); + } + + public Expr left() { + return left; + } + + public Expr right() { + return right; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + right.visit(visitor); + left.visit(visitor); + } else { + left.visit(visitor); + right.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitIfCmpStmt(this); + } + + public Object clone() { + return copyInto(new IfCmpStmt(comparison, (Expr) left.clone(), + (Expr) right.clone(), trueTarget, falseTarget)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/IfStmt.java b/src/edu/purdue/cs/bloat/tree/IfStmt.java new file mode 100644 index 0000000..bddba89 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/IfStmt.java @@ -0,0 +1,140 @@ +/* + * Class: + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * IfStmt is a super class of statements in which some expression is evaluated + * and one of two branches is taken. + * + * @see IfCmpStmt + * @see IfZeroStmt + */ +public abstract class IfStmt extends JumpStmt { + int comparison; // Type of comparison that is performed + + Block trueTarget; // Code to jump to if IfStmt is true + + Block falseTarget; // Code to jump to if IfStmt is false + + // Compairson operators... + public static final int EQ = 0; + + public static final int NE = 1; + + public static final int GT = 2; + + public static final int GE = 3; + + public static final int LT = 4; + + public static final int LE = 5; + + /** + * Constructor. + * + * @param comparison + * Comparison operator used in this if statement. + * @param trueTarget + * Basic Block that is executed when if statement is true. + * @param flaseTarget + * Basic Block that is executed when if statement is false. + */ + public IfStmt(int comparison, Block trueTarget, Block falseTarget) { + this.comparison = comparison; + this.trueTarget = trueTarget; + this.falseTarget = falseTarget; + } + + /** + * @return Comparison operator for this if statement. + */ + public int comparison() { + return comparison; + } + + /** + * Set the comparison operator for this if statement to its logical + * negative. + */ + public void negate() { + switch (comparison) { + case EQ: + comparison = NE; + break; + case NE: + comparison = EQ; + break; + case LT: + comparison = GE; + break; + case GE: + comparison = LT; + break; + case GT: + comparison = LE; + break; + case LE: + comparison = GT; + break; + } + + Block t = trueTarget; + trueTarget = falseTarget; + falseTarget = t; + } + + public void setTrueTarget(Block target) { + this.trueTarget = target; + } + + public void setFalseTarget(Block target) { + this.falseTarget = target; + } + + public Block trueTarget() { + return trueTarget; + } + + public Block falseTarget() { + return falseTarget; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/IfZeroStmt.java b/src/edu/purdue/cs/bloat/tree/IfZeroStmt.java new file mode 100644 index 0000000..4029b35 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/IfZeroStmt.java @@ -0,0 +1,91 @@ +/* + * Class: IfZeroStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * IfZeroStmt evaluates an expression and executes one of its two branches + * depending on whether or not the expression evaluated to zero. + */ +public class IfZeroStmt extends IfStmt { + Expr expr; // Expression to evaluate + + /** + * Constructor. + * + * @param comparison + * Comparison operator. + * @param expr + * An expression to be evaluated. + * @param trueTarget + * Basic Block that is executed if the expression evaluates to + * zero. + * @param flaseTarget + * Basic Block that is executed if the expression evaluates to + * non-zero. + */ + public IfZeroStmt(int comparison, Expr expr, Block trueTarget, + Block falseTarget) { + super(comparison, trueTarget, falseTarget); + this.expr = expr; + expr.setParent(this); + } + + /** + * @return The expression that is evaluated. + */ + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitIfZeroStmt(this); + } + + public Object clone() { + return copyInto(new IfZeroStmt(comparison, (Expr) expr.clone(), + trueTarget, falseTarget)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/InitStmt.java b/src/edu/purdue/cs/bloat/tree/InitStmt.java new file mode 100644 index 0000000..8d81420 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/InitStmt.java @@ -0,0 +1,102 @@ +/* + * Class: InitStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * InitStmt groups together the initialization of local variables (LocalExpr). + * + * @see LocalExpr + * @see Tree#initLocals + */ +public class InitStmt extends Stmt implements Assign { + final LocalExpr[] targets; + + /** + * Constructor. + * + * @param targets + * The instances of LocalExpr that are to be initialized. + */ + public InitStmt(LocalExpr[] targets) { + this.targets = new LocalExpr[targets.length]; + + for (int i = 0; i < targets.length; i++) { + this.targets[i] = targets[i]; + this.targets[i].setParent(this); + } + } + + /** + * Returns the local variables (LocalExprs) initialized by this + * InitStmt. + */ + public LocalExpr[] targets() { + return targets; + } + + /** + * Returns the local variables (LocalExprs) defined by this + * InitStmt. These are the same local variables that are the + * targets of the InitStmt. + */ + public DefExpr[] defs() { + return targets; + } + + public void visitForceChildren(TreeVisitor visitor) { + for (int i = 0; i < targets.length; i++) { + targets[i].visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitInitStmt(this); + } + + public Object clone() { + LocalExpr[] t = new LocalExpr[targets.length]; + + for (int i = 0; i < targets.length; i++) { + t[i] = (LocalExpr) targets[i].clone(); + } + + return copyInto(new InitStmt(t)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/InstanceOfExpr.java b/src/edu/purdue/cs/bloat/tree/InstanceOfExpr.java new file mode 100644 index 0000000..6b57b59 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/InstanceOfExpr.java @@ -0,0 +1,106 @@ +/* + * Class: InstanceOfExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * InstanceOfExpr represnts the instanceof opcode which determine if an + * object is of a given type. + */ +public class InstanceOfExpr extends CondExpr { + // instanceof + + Expr expr; // Expression (object) whose type we verify + + final Type checkType; // Type to verify against + + /** + * Constructor. + * + * @param expr + * Expression (object) whose type is to be verified. + * @param checkType + * Type to verify against (That is, is expr of type checkType?) + * @param type + * Type of this expression. + */ + public InstanceOfExpr(Expr expr, Type checkType, Type type) { + super(type); + this.expr = expr; + this.checkType = checkType; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public Type checkType() { + return checkType; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitInstanceOfExpr(this); + } + + public int exprHashCode() { + return 12 + expr.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof InstanceOfExpr + && ((InstanceOfExpr) other).checkType.equals(checkType) + && ((InstanceOfExpr) other).expr.equalsExpr(expr); + } + + public Object clone() { + return copyInto(new InstanceOfExpr((Expr) expr.clone(), checkType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/JsrStmt.java b/src/edu/purdue/cs/bloat/tree/JsrStmt.java new file mode 100644 index 0000000..723693f --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/JsrStmt.java @@ -0,0 +1,95 @@ +/* + * Class: JsrStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.Subroutine; + +/** + * JsrStmt represents a jsr instruction that jumps to a subroutine. + * Recall that a subroutine is used to implement the finally cause in exception + * handlers. The ret instruction is used to return from a subroutine. + * + * @see RetStmt + * @see Subroutine + */ +public class JsrStmt extends JumpStmt { + Subroutine sub; // Subroutine to which to jump + + Block follow; // Basic Block to execute upon returning + + // from the subroutine + + /** + * Constructor. + * + * @param sub + * Subroutine that this statement jumps to. + * @param follow + * Basic Block following the jump statement. + */ + public JsrStmt(Subroutine sub, Block follow) { + this.sub = sub; + this.follow = follow; + } + + public void setFollow(Block follow) { + this.follow = follow; + } + + public Block follow() { + return follow; + } + + public Subroutine sub() { + return sub; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitJsrStmt(this); + } + + public Object clone() { + return copyInto(new JsrStmt(sub, follow)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/JumpStmt.java b/src/edu/purdue/cs/bloat/tree/JumpStmt.java new file mode 100644 index 0000000..b43cbf5 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/JumpStmt.java @@ -0,0 +1,74 @@ +/* + * Class: JumpStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * JumpStmt is the super class for several classes that represent + * statements that chang the flow of control in a program. + * + * @see GotoStmt + * @see IfStmt + * @see JsrStmt + */ +public abstract class JumpStmt extends Stmt { + Set catchTargets; + + public JumpStmt() { + catchTargets = new LinkedHashSet(); + } + + /** + * The Block containing this JumpStmt may lie within a + * try block (i.e. it is a protected block). If so, then + * catchTargets() returns a set of Blocks that begin + * the exception handler for the exception that may be thrown in the + * protected block. + */ + public Collection catchTargets() { + return catchTargets; + } + + protected Node copyInto(Node node) { + ((JumpStmt) node).catchTargets.addAll(catchTargets); + return super.copyInto(node); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/LEGatherer.java b/src/edu/purdue/cs/bloat/tree/LEGatherer.java new file mode 100644 index 0000000..819c829 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/LEGatherer.java @@ -0,0 +1,69 @@ +/* + * Class: LEGatherer + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * LEGatherer visits a basic block and returns all the LocalExprs in a vector + * + * @author Thomas VanDrunen + */ + +public class LEGatherer extends TreeVisitor { + + Vector LEs; + + Vector getLEs(Block b) { + + LEs = new Vector(); + + visitBlock(b); + + return LEs; + } + + public void visitLocalExpr(LocalExpr expr) { + LEs.addElement(expr); + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/LabelStmt.java b/src/edu/purdue/cs/bloat/tree/LabelStmt.java new file mode 100644 index 0000000..686009f --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/LabelStmt.java @@ -0,0 +1,98 @@ +/* + * Class: LabelStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.InstructionHandle; + +import edu.purdue.cs.bloat.cfg.FlowGraph; + +/** + * LabelStmt is a placeholder in a Tree for a Label (the target of a jump). + * + * BLOAT has a Label instruction the purpose of which is to denote the target of + * a jump or BranchInstruction, BCEL has no such instruction. Many of the + * optimizations in BLOAT and FlowGraph depend on theLabels being present. To + * mimick the presence of Labels the BCEL port uses the method + * buildBLOATInstructionList(MethodGen) in FlowGraph, this sets + * InstructionHandle attributes using the key "Label". The methods setLabel and + * unsetLabel are supplied to manipulate this state and the method label returns + * a boolean true if the instruction is also a label. This has one complication + * not present in BLOAT, that is an instruction in the port can be a "Label" + * (FlowGraph.label() returns true) and also have an instruction which serves a + * useful purpose(is not a NOP instruction). When the FlowGraph is commited in + * CodeGenerator, the BCEL port uses NOP instructions to commit LabelStmts. For + * robustness later optimizations performed on bytecode (Peephole and StackOpt) + * do not rely on labels also being NOP instructions. --Arrin + * + * @see InstructionHandle + * @see FlowGraph#label + * @see FlowGraph#setLabel + * @see FlowGraph#unsetLabel + * @see FlowGraph#buildBLOATInstructionList + * @see Tree#addLabel + */ +public class LabelStmt extends Stmt { + final InstructionHandle label; + + /** + * Constructor. + * + * @param label + * The label(InstructionHandle) that comprises this statement. + */ + public LabelStmt(InstructionHandle label) { + this.label = label; + } + + public InstructionHandle label() { + return label; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitLabelStmt(this); + } + + public Object clone() { + return copyInto(new LabelStmt(label)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/LeafExpr.java b/src/edu/purdue/cs/bloat/tree/LeafExpr.java new file mode 100644 index 0000000..1a69448 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/LeafExpr.java @@ -0,0 +1,51 @@ +/* + * Class: LeafExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * An expression that can appear as a leaf node in a Tree. Essentially, LeafExpr + * can not have child expressions. + * + * @see ConstantExpr + * @see LocalExpr + * + */ +public interface LeafExpr { +} diff --git a/src/edu/purdue/cs/bloat/tree/LocalExpr.java b/src/edu/purdue/cs/bloat/tree/LocalExpr.java new file mode 100644 index 0000000..bb70426 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/LocalExpr.java @@ -0,0 +1,124 @@ +/* + * Class: LocalExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; +import org.apache.bcel.generic.ReturnaddressType; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * LocalExpr represents an expression that accesses a variable in a method's + * local variable table. Note that during register allocation the index becomes + * the color that the LocalExpr (variable) is assigned. + * + * @see Tree#newStackLocal + * @see Tree#newLocal + */ +public class LocalExpr extends VarExpr implements LeafExpr { + boolean fromStack; + + /** + * Constructor. + * + * @param index + * Index into the local variable table for this expression. + * @param fromStack + * Is the local allocated on the stack? + * @param type + * The type of this expression + */ + public LocalExpr(int index, boolean fromStack, Type type) { + super(index, type); + this.fromStack = fromStack; + } + + /** + * Constructor. LocalExpr is not allocated on the stack. + * + * @param index + * Index into the local variable table for this expression. + * @param type + * The type of this expression. + */ + public LocalExpr(int index, Type type) { + this(index, false, type); + } + + public boolean fromStack() { + return fromStack; + } + + /** + * Returns true if the type of this expression is a return address. + */ + public boolean isReturnAddress() { + return type().equals(ReturnaddressType.NO_TARGET); + } // DODGYNESS HERE ^ + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitLocalExpr(this); + } + + /** + * @param other + * The other expression to compare against. + */ + public boolean equalsExpr(Expr other) { + return other instanceof LocalExpr + && Assert.simple(((LocalExpr) other).type).equals( + Assert.simple(type)) + && ((LocalExpr) other).fromStack == fromStack + && ((LocalExpr) other).index == index; + } + + public int exprHashCode() { + return 13 + (fromStack ? 0 : 1) + index + + (int) Assert.simple(type).hashCode(); + } + + public Object clone() { + return copyInto(new LocalExpr(index, fromStack, type)); + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/Makefile b/src/edu/purdue/cs/bloat/tree/Makefile new file mode 100644 index 0000000..6cba689 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Makefile @@ -0,0 +1,99 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + AddressStoreStmt.class\ + ArithExpr.class\ + ArrayLengthExpr.class\ + ArrayRefExpr.class\ + AscendVisitor.class\ + Assign.class\ + CallExpr.class\ + CallMethodExpr.class\ + CallStaticExpr.class\ + CastExpr.class\ + CatchExpr.class\ + CheckExpr.class\ + CondExpr.class\ + ConstantExpr.class\ + DescendVisitor.class\ + DefExpr.class\ + Expr.class\ + ExprStmt.class\ + FieldExpr.class\ + GotoStmt.class\ + IfCmpStmt.class\ + IfStmt.class\ + IfZeroStmt.class\ + InitStmt.class\ + InstanceOfExpr.class\ + JsrStmt.class\ + JumpStmt.class\ + LabelStmt.class\ + LeafExpr.class\ + LEGatherer.class\ + LocalExpr.class\ + MemExpr.class\ + MemRefExpr.class\ + MonitorStmt.class\ + NegExpr.class\ + NewArrayExpr.class\ + NewExpr.class\ + NewMultiArrayExpr.class\ + Node.class\ + OperandStack.class\ + PhiCatchStmt.class\ + PhiJoinStmt.class\ + PhiStmt.class\ + PrintVisitor.class\ + RCExpr.class\ + ReplaceVisitor.class\ + RetStmt.class\ + ReturnAddressExpr.class\ + ReturnExprStmt.class\ + ReturnStmt.class\ + SCStmt.class\ + SRStmt.class\ + ShiftExpr.class\ + StackExpr.class\ + StackManipStmt.class\ + StaticFieldExpr.class\ + StackOptimizationData.class\ + Stmt.class\ + StoreExpr.class\ + SwitchStmt.class\ + Swizzler.class\ + ThrowStmt.class\ + Tree.class\ + TreeVisitor.class\ + Type0Visitor.class\ + Type1Visitor.class\ + UCExpr.class\ + VarExpr.class\ + ZeroCheckExpr.class + +include ../class.mk + + + + + + diff --git a/src/edu/purdue/cs/bloat/tree/MemExpr.java b/src/edu/purdue/cs/bloat/tree/MemExpr.java new file mode 100644 index 0000000..04f1b99 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/MemExpr.java @@ -0,0 +1,51 @@ +/* + * Class: MemExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * An expression that accesses a memory location. + */ +public abstract class MemExpr extends DefExpr { + public MemExpr(Type type) { + super(type); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/MemRefExpr.java b/src/edu/purdue/cs/bloat/tree/MemRefExpr.java new file mode 100644 index 0000000..b3f325e --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/MemRefExpr.java @@ -0,0 +1,56 @@ +/* + * Class: MemRefExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * MemRefExpr represents an expression that references a memory location as + * opposed to a local variable or a variable on the stack. + * + * @see ArrayRefExpr + * @see FieldExpr + * @see StackExpr + */ +public abstract class MemRefExpr extends MemExpr { + public MemRefExpr(Type type) { + super(type); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/MonitorStmt.java b/src/edu/purdue/cs/bloat/tree/MonitorStmt.java new file mode 100644 index 0000000..ddff018 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/MonitorStmt.java @@ -0,0 +1,91 @@ +/* + * Class: MonitorStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * MonitorStmt represents the monitorenter and monitorexit + * opcodes, which gain and release ownership of the monitor associated with a + * given object. + */ +public class MonitorStmt extends Stmt { + public static final int ENTER = 0; + + public static final int EXIT = 1; + + final int kind; + + Expr object; + + /** + * Constructor. + * + * @param kind + * The kind of monitor statement: ENTER or EXIT. + * @param object + * The expression (object) whose monitor is being entered or + * exited. + */ + public MonitorStmt(int kind, Expr object) { + this.kind = kind; + this.object = object; + + object.setParent(this); + } + + public Expr object() { + return object; + } + + public int kind() { + return kind; + } + + public void visitForceChildren(TreeVisitor visitor) { + object.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitMonitorStmt(this); + } + + public Object clone() { + return copyInto(new MonitorStmt(kind, (Expr) object.clone())); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/NegExpr.java b/src/edu/purdue/cs/bloat/tree/NegExpr.java new file mode 100644 index 0000000..335e9c2 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/NegExpr.java @@ -0,0 +1,92 @@ +/* + * Class: NegExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * NegExpr represents the arithmetic negation of an expression. + */ +public class NegExpr extends Expr { + Expr expr; + + /** + * Constructor. + * + * @param expr + * The expression to be negated. + * @param type + * The type of this expression. + */ + public NegExpr(Expr expr, Type type) { + super(type); + this.expr = expr; + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitNegExpr(this); + } + + public int exprHashCode() { + return 14 + expr.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof NegExpr + && ((NegExpr) other).expr.equalsExpr(expr); + } + + public Object clone() { + return copyInto(new NegExpr((Expr) expr.clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/NewArrayExpr.java b/src/edu/purdue/cs/bloat/tree/NewArrayExpr.java new file mode 100644 index 0000000..9aefbe9 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/NewArrayExpr.java @@ -0,0 +1,108 @@ +/* + * Class: NewArrayExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * NewArrayExpr represents the newarray opcode which creates a new + * array of a specified length and element type. + */ +public class NewArrayExpr extends Expr { + // newarray + + Expr size; + + final Type elementType; + + /** + * Constructor. + * + * @param size + * Expression representing the size of the array. + * @param elementType + * The type of the elements in the array. + * @param type + * The type of this expression. + * + * This Expr represents an allocation for single dimensional array, + * multi-dimensional arrays use NewMultiArrayExpr. + * @see NewMultiArrayExpr + */ + public NewArrayExpr(Expr size, Type elementType, Type type) { + super(type); + this.size = size; + this.elementType = elementType; + + size.setParent(this); + } + + public Expr size() { + return size; + } + + public Type elementType() { + return elementType; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + size.visit(visitor); + } else { + size.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitNewArrayExpr(this); + } + + public int exprHashCode() { + return 15 + size.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + return copyInto(new NewArrayExpr((Expr) size.clone(), elementType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/NewExpr.java b/src/edu/purdue/cs/bloat/tree/NewExpr.java new file mode 100644 index 0000000..1764269 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/NewExpr.java @@ -0,0 +1,90 @@ +/* + * Class: NewExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * NewExpr represents the new opcode that creates a new object of a + * specified type. + */ +public class NewExpr extends Expr { + // new + final Type objectType; + + /** + * Constructor. + * + * @param objectType + * The type of the object to create. + * @param type + * The type of this expression. + */ + public NewExpr(Type objectType, Type type) { + super(type); + this.objectType = objectType; + } + + /** + * Returns the Type of the object being created. + */ + public Type objectType() { + return objectType; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitNewExpr(this); + } + + public int exprHashCode() { + return 16 + objectType.hashCode(); + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + return copyInto(new NewExpr(objectType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/NewMultiArrayExpr.java b/src/edu/purdue/cs/bloat/tree/NewMultiArrayExpr.java new file mode 100644 index 0000000..97ee8a5 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/NewMultiArrayExpr.java @@ -0,0 +1,123 @@ +/* + * Class: NewMultiArrayExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * NewMultiArrayExpr represents the multianewarray opcode which + * creates a new multidimensional array. + */ +public class NewMultiArrayExpr extends Expr { + // multianewarray + + final Expr[] dimensions; + + final Type elementType; + + /** + * Constructor. + * + * @param dimensions + * Expressions representing the size of each of the dimensions in + * the array. + * @param elementType + * The type of the elements in the array. + * @param type + * The type of this expression. + */ + public NewMultiArrayExpr(Expr[] dimensions, Type elementType, Type type) { + super(type); + this.elementType = elementType; + this.dimensions = dimensions; + + for (int i = 0; i < dimensions.length; i++) { + dimensions[i].setParent(this); + } + } + + public Expr[] dimensions() { + return dimensions; + } + + public Type elementType() { + return elementType; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + for (int i = dimensions.length - 1; i >= 0; i--) { + dimensions[i].visit(visitor); + } + } else { + for (int i = 0; i < dimensions.length; i++) { + dimensions[i].visit(visitor); + } + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitNewMultiArrayExpr(this); + } + + public int exprHashCode() { + int v = 17; + + for (int i = 0; i < dimensions.length; i++) { + v ^= dimensions[i].hashCode(); + } + + return v; + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + Expr[] d = new Expr[dimensions.length]; + + for (int i = 0; i < dimensions.length; i++) { + d[i] = (Expr) dimensions[i].clone(); + } + + return copyInto(new NewMultiArrayExpr(d, elementType, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Node.java b/src/edu/purdue/cs/bloat/tree/Node.java new file mode 100644 index 0000000..8749dec --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Node.java @@ -0,0 +1,297 @@ +/* + * Class: Node + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.io.*; +import org.apache.bcel.generic.BasicType; +import org.apache.bcel.generic.ReferenceType; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Node represents a node in an expression tree. Each Node has a value number + * and a key associated with it. The value number is used to eliminate + * statements and expressions that have the same value (PRE). Statements and + * expressions of the same value will be mapped to the same value number. + * + * @see Expr + * @see Stmt + * @see Tree + */ +public abstract class Node { + protected Node parent; // This Node's parent in a Tree. + + int key; // integer used in some analyses. For instance, when + + // dead code elimination is performed, key is set to + // DEAD or LIVE. + int valueNumber; // Used in eliminating redundent statements + + /** + * Constructor. + */ + public Node() { + // if (Tree.DEBUG) { + // // We can't print the Node because things aren't initialized yet + // System.out.println(" new node " + + // System.identityHashCode(this)); + // } + + parent = null; + valueNumber = -1; + key = 0; + } + + /** + * Returns this Node's value number. + */ + public int valueNumber() { + return valueNumber; + } + + /** + * Sets this Node's value number. + */ + public void setValueNumber(int valueNumber) { + // if (Tree.DEBUG) { + // System.out.println(" setVN[" + this + "] = " + valueNumber); + // } + this.valueNumber = valueNumber; + } + + /** + * A Node's key represents an integer value that can be used by an algorithm + * to mark this node. For instance, when dead code elimination is performed + * a Node is marked as DEAD or ALIVE. + */ + public int key() { + return key; + } + + public void setKey(int key) { + this.key = key; + } + + /** + * Visit the children of this node. Not all Nodes will have children to + * visit. + */ + public abstract void visitForceChildren(TreeVisitor visitor); + + public abstract void visit(TreeVisitor visitor); + + public void visitChildren(TreeVisitor visitor) { + if (!visitor.prune()) { + visitForceChildren(visitor); + } + } + + public void visitOnly(TreeVisitor visitor) { + visitor.setPrune(true); + visit(visitor); + visitor.setPrune(false); + } + + /** + * Returns the basic block in which this Node resides. + */ + public Block block() { + Node p = this; + + while (p != null) { + if (p instanceof Tree) { + return ((Tree) p).block(); + } + + p = p.parent; + } + + throw new RuntimeException(this + " is not in a block"); + } + + /** + * Sets the parent Node of this Node. + */ + public void setParent(Node parent) { + // if (Tree.DEBUG) { + // System.out.println(" setting parent of " + this + " (" + + // System.identityHashCode(this) + ") to " + parent); + // } + + this.parent = parent; + } + + public boolean hasParent() { + return parent != null; + } + + public Node parent() { + // Note that we can't print this Node because of recursion. Sigh. + Assert.isTrue(parent != null, "Null parent for " + + this.getClass().toString() + " node " + + System.identityHashCode(this)); + return parent; + } + + /** + * Copies the contents of one Node into another. + * + * @param node + * A Node from which to copy. + * + * @return node containing the contents of this Node. + */ + protected Node copyInto(Node node) { + node.setValueNumber(valueNumber); + return node; + } + + /** + * Clean up this Node only. Does not effect its children nodes. + */ + public abstract void cleanupOnly(); + + /** + * Cleans up this node so that it is independent of the expression tree in + * which it resides. This is usually performed before a Node is moved from + * one part of an expression tree to another. + *

      + * Traverse the Tree starting at this Node. Remove the parent of each node + * and perform any Node-specific cleanup (see cleanupOnly). Sets various + * pointers to null so that they eventually may be garbage collected. + */ + public void cleanup() { + // if (Tree.DEBUG) { + // System.out.println(" CLEANING UP " + this + " " + + // System.identityHashCode(this)); + // } + + visit(new TreeVisitor() { + public void visitNode(Node node) { + node.setParent(null); + node.cleanupOnly(); + node.visitChildren(this); + } + }); + } + + /** + * Replaces this node with another and perform cleanup. + */ + public void replaceWith(Node node) { + replaceWith(node, true); + } + + /** + * Replaces this Node with node in its (this's) tree. + * + * @param node + * The Node with which to replace. + * @param cleanup + * Do we perform cleanup on the tree? + */ + public void replaceWith(Node node, boolean cleanup) { + // Check a couple of things: + // 1. The node with which we are replace this does not have a parent. + // 2. This Node does have a parent. + Assert.isTrue(node.parent == null, node + " already has a parent"); + Assert.isTrue(parent != null, this + " has no parent"); + + Node oldParent = parent; + + if (this instanceof Stmt) { + Assert.isTrue(node instanceof Stmt, "Attempt to replace " + this + + " with " + node); + } + + if (this instanceof Expr) { + Assert.isTrue(node instanceof Expr, "Attempt to replace " + this + + " with " + node); + + Expr expr1 = (Expr) this; + Expr expr2 = (Expr) node; + + // Make sure the expressions can be interchanged (i.e. their + // descriptors + // are compatible). + // MOD + Assert + .isTrue( + (((expr1.type() instanceof BasicType) && (expr2 + .type() instanceof BasicType)) || ((expr1 + .type() instanceof ReferenceType) && (expr2 + .type() instanceof ReferenceType))), + "Type mismatch when replacing " + expr1 + " with " + + expr2 + ": " + expr1.type() + " != " + + expr2.type()); + } + + // Iterate over this parent's tree and replace this with node. + parent.visit(new ReplaceVisitor(this, node)); + + Assert.isTrue(node.parent == oldParent, node + " parent == " + + node.parent + " != " + oldParent); + + if (cleanup) { + cleanup(); + } + } + + /** + * @return A textual representation of this Node. + */ + public String toString() { + StringWriter w = new StringWriter(); + + visit(new PrintVisitor(w) { + protected void println(Object s) { + print(s); + } + + protected void println() { + } + }); + + w.flush(); + + return w.toString(); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/OperandStack.java b/src/edu/purdue/cs/bloat/tree/OperandStack.java new file mode 100644 index 0000000..b582e65 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/OperandStack.java @@ -0,0 +1,286 @@ +/* + * Class: OperandStack + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; +import org.apache.bcel.generic.Type; +import org.apache.bcel.generic.ReferenceType; +import org.apache.bcel.generic.ReturnaddressType; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * OperandStack is used to simulate the JVM stack. A stack of + * expressions is maintained. OperandStack has methods to push and + * pop (both wide and non-wide) expressions, replace an expression at a given + * depth in the stack, peek into the stack, etc. + * + * @see Expr + * @see Tree + */ +public class OperandStack { + ArrayList stack; // The contents of the stack + + int height; // The height of the stack (i.e. the number + + // of elements in the stack) + + /** + * Constructor. + */ + public OperandStack() { + stack = new ArrayList(); + height = 0; + } + + /** + * @return True, if the stack is empty. + */ + public boolean isEmpty() { + return stack.isEmpty(); + } + + /** + * Pops an operand off the stack. Checks to make sure the top of the stack + * is of the expected Type. + * + * @param type + * The expected Type of the top of the stack + * @return Expression on the top of the stack + */ + public Expr pop(Type type) { + if (Tree.DEBUG) + System.out.println("Trying to Pop type: " + type + "(" + + type.getSize() + ") height = " + height); + + Expr top = (Expr) stack.remove(stack.size() - 1); + + Type topType = top.type(); + + height -= topType.getSize(); + + if (Tree.DEBUG) + System.out.println("Poped type: " + top.type() + "(" + + top.type().getSize() + ") height = " + height); + + if (type instanceof ReturnaddressType) { + if (!(topType instanceof ReturnaddressType)) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + } else if (type instanceof ReferenceType) { + if (!(topType instanceof ReferenceType)) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + } else if (Assert.isIntegral(type)) { + if (!Assert.isIntegral(topType)) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + } else if (!type.equals(topType)) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + + return top; + } + + /** + * Returns the expression at the top of the stack, but does not modify the + * stack. + */ + public Expr peek() { + return (Expr) stack.get(stack.size() - 1); + } + + /** + * Sets the entry at a specified index from the bottom of the stack + * + * @param index + * The position in the stack. + * @param expr + * The new value of the expression. + */ + public void set(int index, Expr expr) { + stack.set(index, expr); + } + + /** + * @return The number of elements in the stack. + */ + public int height() { + return height; + } + + /** + * Replaces the expression that is depth expressions from the top of the + * stack. + * + * @param depth + * The number of expressions from the top of the stack. + * + * @param expr + * The new expression + */ + public void replace(int depth, Expr expr) { + for (int i = stack.size() - 1; i >= 0; i--) { + Expr top = (Expr) stack.get(i); + + if (depth == 0) { + stack.set(i, expr); + return; + } + + depth -= top.type().getSize(); + } + + throw new IllegalArgumentException("Can't replace below stack bottom."); + } + + /** + * Get the expression that is depth expressions from the top of the stack, + * but do not modify the stack. + * + * @param depth + * Number of expressions deep to get. + * + * @return The expression that is depth expression from the top of the + * stack. + */ + public Expr peek(int depth) { + int d = depth; + for (int i = stack.size() - 1; i >= 0; i--) { + Expr top = (Expr) stack.get(i); + System.out.println("top = " + top); + if (d == 0) { + return top; + } + + d = d - top.type().getSize(); + } + + throw new IllegalArgumentException("Can't peek below stack bottom."); + } + + /** + * Pops a non-wide expression off the stack. + */ + public Expr pop1() { + if (Tree.DEBUG) + System.out.println("Trying to Pop1 (" + 1 + ") height = " + height); + + Expr top = (Expr) stack.remove(stack.size() - 1); + + Type type = top.type(); + + if (Tree.DEBUG) + System.out.println("Poped type: " + top.type() + "(" + + top.type().getSize() + ") height = " + height); + + if (type.getSize() == 2) { + throw new IllegalArgumentException("Expected a word " + + ", got a long"); + } + + height--; + + return top; + } + + /** + * Pops a (possibly) wide expression off of the stack and returns the result + * as an array of Expr. If the expression at the top of the + * stack is indeed wide, it is returned in element [0] of the array. If the + * expression at the top of the stack is not wide, the top two + * expressions are returned in the array as elements 0 and 1. + */ + public Expr[] pop2() { + Expr top = (Expr) stack.remove(stack.size() - 1); + Expr[] a; + + Type type = top.type(); + + if (type.getSize() == 2) { + a = new Expr[1]; + a[0] = top; + } else { + a = new Expr[2]; + a[0] = (Expr) stack.remove(stack.size() - 1); + a[1] = top; + } + + height -= 2; + + return a; + } + + /** + * Push an expression onto the stack. + * + * @see edu.purdue.cs.bloat.editor.Type#stackHeight + */ + public void push(Expr expr) { + height += expr.type().getSize(); + stack.add(expr); + } + + /** + * Returns the number of expressions on the stack. + */ + public int size() { + return stack.size(); + } + + /** + * Returns the expression at index from the bottom of the stack. + */ + public Expr get(int index) { + Expr expr = (Expr) stack.get(index); + return expr; + } + + /** + * Returns a String represntation of this stack. + */ + public String toString() { + return stack.toString(); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/PhiCatchStmt.java b/src/edu/purdue/cs/bloat/tree/PhiCatchStmt.java new file mode 100644 index 0000000..ba23910 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/PhiCatchStmt.java @@ -0,0 +1,173 @@ +/* + * Class: PhiCatchStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * A PhiCatchStmt is used to handle variables that are used inside an exception + * handler. Inside a try block a variable may be used several times. It may be + * updated, may be invovled in a phi-function, etc. A PhiCatchStmt is placed at + * the beginning of each expection handling (catch) block to factor together the + * variables that are live within the protected region. + */ +public class PhiCatchStmt extends PhiStmt { + final ArrayList operands; + + /** + * Constructor. + * + * @param target + * Local variable to which the result of this phi-function is to + * be assigned. + */ + public PhiCatchStmt(LocalExpr target) { + super(target); + this.operands = new ArrayList(); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + target.visit(visitor); + } + + for (int i = 0; i < operands.size(); i++) { + LocalExpr expr = (LocalExpr) operands.get(i); + expr.visit(visitor); + } + + if (!visitor.reverse()) { + target.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitPhiCatchStmt(this); + } + + /** + * Searches the list of operands for a local variable. + * + * @param def + * The local variable definition to search for. + * @returns True, if def is found, otherwise, false. + */ + public boolean hasOperandDef(LocalExpr def) { + for (int i = 0; i < operands.size(); i++) { + LocalExpr expr = (LocalExpr) operands.get(i); + if (expr.def() == def) { + return true; + } + } + + return false; + } + + /** + * Add a local variable to the operand list for this phi-function. + * + * @param operand + * An operand of this phi-function. + */ + public void addOperand(LocalExpr operand) { + for (int i = 0; i < operands.size(); i++) { + LocalExpr expr = (LocalExpr) operands.get(i); + Assert.isTrue(expr.def() != operand.def()); + } + + operands.add(operand); + operand.setParent(this); + } + + /** + * Returns the operands to this phi-function. + */ + public Collection operands() { + if (operands == null) { + return new ArrayList(); + } + + for (int i = 0; i < operands.size(); i++) { + LocalExpr ei = (LocalExpr) operands.get(i); + + for (int j = operands.size() - 1; j > i; j--) { + LocalExpr ej = (LocalExpr) operands.get(j); + + if (ei.def() == ej.def()) { + ej.cleanup(); + operands.remove(j); + } + } + } + + return operands; + } + + /** + * Returns the number of operands to this phi-function. + */ + public int numOperands() { + return operands.size(); + } + + /** + * Sets the value of one of this phi-function's operands. + * + * @param i + * The number parameter to set. + * @param expr + * The new value of the parameter. + */ + public void setOperandAt(int i, Expr expr) { + Expr old = (Expr) operands.get(i); + old.cleanup(); + operands.set(i, expr); + expr.setParent(this); + } + + /** + * Returns the operand at a given index. + */ + public Expr operandAt(int i) { + return (Expr) operands.get(i); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/PhiJoinStmt.java b/src/edu/purdue/cs/bloat/tree/PhiJoinStmt.java new file mode 100644 index 0000000..c21c8be --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/PhiJoinStmt.java @@ -0,0 +1,171 @@ +/* + * Class: PhiJoinStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * PhiJoinStmt represents a phi-function inserted into a control flow + * graph during conversion of variables to static single-assignment form. A + * PhiJoinStmt at a point of control flow convergence. + * + * @see edu.purdue.cs.bloat.ssa.SSAConstructionInfo SSAConstructionInfo + */ +public class PhiJoinStmt extends PhiStmt { + final Map operands; // Operands to a PhiStmt (mapping between a Block + + // and a VarExpr occurring at that block) + final Block block; // Basic block containing this PhiJoinStmt + + /** + * Constructor. + * + * @param target + * The target of this PhiStmt. + * @param block + * The basic Block in which this PhiJoinStmt resides. + */ + public PhiJoinStmt(VarExpr target, Block block) { + super(target); + + this.block = block; + this.operands = new LinkedHashMap(); + + Iterator preds = block.graph().preds(block).iterator(); + + while (preds.hasNext()) { + Block pred = (Block) preds.next(); + VarExpr operand = (VarExpr) target.clone(); + operands.put(pred, operand); + operand.setParent(this); + operand.setDef(null); + } + } + + /** + * Set the operand to this PhiJoinStmt for a given Block to a given + * expression. + * + * @param block + * + * @param expr + * + */ + public void setOperandAt(Block block, Expr expr) { + Expr operand = (Expr) operands.get(block); + + if (operand != null) { + operand.cleanup(); + } + + if (expr != null) { + operands.put(block, expr); + expr.setParent(this); + } else { + operands.remove(block); + } + } + + /** + * Returns the occurrence of the variable with which this PhiJoinStmt is + * concerned (usually represented by a VarExpr) at a given block. + * + * @param block + * The block at which an occurrence of the variable occurs. + * + * @see VarExpr + */ + public Expr operandAt(Block block) { + return (Expr) operands.get(block); + } + + /** + * Returns the number of operands that this PhiJoinStmt has. + */ + public int numOperands() { + return block.graph().preds(block).size(); + } + + /** + * Returns the predacessor nodes (in the CFG not dominator graph) of the + * block in which this PhiJoinStmt occurs. + */ + public Collection preds() { + return block.graph().preds(block); + } + + /** + * Returns the operands of this PhiJoinStmt. They are usually of type + * VarExpr. + * + * @see VarExpr + */ + public Collection operands() { + if (operands != null) { + operands.keySet().retainAll(preds()); + return operands.values(); + } + + return new ArrayList(); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + target.visit(visitor); + } + + Iterator e = operands().iterator(); + + while (e.hasNext()) { + Expr operand = (Expr) e.next(); + operand.visit(visitor); + } + + if (!visitor.reverse()) { + target.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitPhiJoinStmt(this); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/PhiStmt.java b/src/edu/purdue/cs/bloat/tree/PhiStmt.java new file mode 100644 index 0000000..d0ee765 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/PhiStmt.java @@ -0,0 +1,83 @@ +/* + * Class: PhiStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * A PhiStmt is inserted into a CFG in Single Static Assignment for. It is used + * to "merge" uses of the same variable in different basic blocks. + * + * @see PhiJoinStmt + * @see PhiCatchStmt + */ +public abstract class PhiStmt extends Stmt implements Assign { + VarExpr target; // The variable into which the Phi statement assigns + + /** + * Constructor. + * + * @param target + * A stack expression or local variable that is the target of + * this phi-statement. + */ + public PhiStmt(VarExpr target) { + this.target = target; + target.setParent(this); + } + + public VarExpr target() { + return target; + } + + /** + * Return the expressions (variables) defined by this PhiStmt. In this case, + * only the target is defined. + */ + public DefExpr[] defs() { + return new DefExpr[] { target }; + } + + public abstract Collection operands(); + + public Object clone() { + throw new RuntimeException(); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/PrintVisitor.java b/src/edu/purdue/cs/bloat/tree/PrintVisitor.java new file mode 100644 index 0000000..91e97c0 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/PrintVisitor.java @@ -0,0 +1,727 @@ +/* + * Class: PrintVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.io.*; +import java.util.*; +import org.apache.bcel.generic.ReferenceType; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.cfg.Handler; + +/** + * PrintVistor traverses a Tree and prints some information about each visited + * Node to a stream. + */ +public class PrintVisitor extends TreeVisitor { + + protected PrintWriter out; // The stream to which we are printing + + /** + * Constructor. Prints to System.out. + */ + public PrintVisitor() { + this(System.out); + } + + public PrintVisitor(Writer out) { + this.out = new PrintWriter(out); + } + + public PrintVisitor(PrintStream out) { + this.out = new PrintWriter(out); + } + + protected void println() { + out.println(); + } + + protected void println(Object s) { + out.println(s); + } + + protected void print(Object s) { + out.print(s); + } + + public void visitFlowGraph(FlowGraph cfg) { + cfg.source().visit(this); + + Iterator e = cfg.trace().iterator(); + + while (e.hasNext()) { + Block block = (Block) e.next(); + block.visit(this); + } + + cfg.sink().visit(this); + + this.out.flush(); + } + + public void visitBlock(Block block) { + println(); + println(block); + + Handler handler = (Handler) block.graph().handlersMap().get(block); + + if (handler != null) { + println("catches " + handler.catchType()); + println("protects " + handler.protectedBlocks()); + } + + block.visitChildren(this); + } + + public void visitExprStmt(ExprStmt stmt) { + print("eval "); + stmt.expr().visit(this); + println(); + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + print("if0 ("); + stmt.expr().visit(this); + print(" "); + + switch (stmt.comparison()) { + case IfStmt.EQ: + print("=="); + break; + case IfStmt.NE: + print("!="); + break; + case IfStmt.GT: + print(">"); + break; + case IfStmt.GE: + print(">="); + break; + case IfStmt.LT: + print("<"); + break; + case IfStmt.LE: + print("<="); + break; + } + + if (stmt.expr().type() instanceof ReferenceType) { + print(" null"); + } else { + print(" 0"); + } + + print(") then " + stmt.trueTarget() + " else " + stmt.falseTarget()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + print("if ("); + stmt.left().visit(this); + print(" "); + + switch (stmt.comparison()) { + case IfStmt.EQ: + print("=="); + break; + case IfStmt.NE: + print("!="); + break; + case IfStmt.GT: + print(">"); + break; + case IfStmt.GE: + print(">="); + break; + case IfStmt.LT: + print("<"); + break; + case IfStmt.LE: + print("<="); + break; + } + + print(" "); + + if (stmt.right() != null) { + stmt.right().visit(this); + } + + print(") then " + stmt.trueTarget() + " else " + stmt.falseTarget()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitInitStmt(InitStmt stmt) { + print("INIT"); + + LocalExpr[] t = stmt.targets(); + + if (t != null) { + for (int i = 0; i < t.length; i++) { + if (t[i] != null) { + print(" "); + t[i].visit(this); + } + } + } + + println(); + } + + public void visitGotoStmt(GotoStmt stmt) { + print("goto " + stmt.target().label()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitLabelStmt(LabelStmt stmt) { + if (stmt.label() != null) { + println("Label: " + stmt.label().getPosition()); + } + } + + public void visitMonitorStmt(MonitorStmt stmt) { + if (stmt.kind() == MonitorStmt.ENTER) { + print("enter "); + } else { + print("exit "); + } + + print("monitor ("); + + if (stmt.object() != null) + stmt.object().visit(this); + + println(")"); + } + + public void visitCatchExpr(CatchExpr expr) { + print("Catch(" + expr.catchType() + ")"); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + print("("); + + StackExpr[] target = stmt.target(); + + if (target != null) { + for (int i = 0; i < target.length; i++) { + target[i].visit(this); + if (i != target.length - 1) { + print(", "); + } + } + } + + String[] str = new String[] { "swap", "dup", "dup_x1", "dup_x2", + "dup2", "dup2_x1", "dup2_x2" }; + + print(") := " + str[stmt.kind()] + "("); + + StackExpr[] source = stmt.source(); + + if (source != null) { + for (int i = 0; i < source.length; i++) { + source[i].visit(this); + if (i != source.length - 1) { + print(", "); + } + } + } + + println(")"); + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + if (stmt.target() != null) { + stmt.target().visit(this); + } + + print(" := Phi("); + + if (stmt.hasParent()) { + Tree tree = (Tree) stmt.parent(); + Block block = tree.block(); + + Iterator e = block.graph().preds(block).iterator(); + + while (e.hasNext()) { + Block pred = (Block) e.next(); + + Expr operand = stmt.operandAt(pred); + print(pred.label() + "="); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + } else { + Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + Expr operand = (Expr) e.next(); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + } + + println(")"); + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + if (stmt.target() != null) { + stmt.target().visit(this); + } + + print(" := Phi-Catch("); + + Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + Expr operand = (Expr) e.next(); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + + println(")"); + } + + public void visitRetStmt(RetStmt stmt) { + print("ret from " + stmt.sub()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + print("return "); + + if (stmt.expr() != null) { + stmt.expr().visit(this); + } + + println(" caught by " + stmt.catchTargets()); + } + + public void visitReturnStmt(ReturnStmt stmt) { + print("return"); + println(" caught by " + stmt.catchTargets()); + } + + public void visitStoreExpr(StoreExpr expr) { + print("("); + + if (expr.target() != null) + expr.target().visit(this); + + print(" := "); + + if (expr.expr() != null) + expr.expr().visit(this); + + print(")"); + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + print("La"); + + if (stmt.sub() != null) + print(new Integer(stmt.sub().returnAddress().getTarget() + .getPosition())); + // Position may not be stable + else + print("???"); + + println(" := returnAddress"); + } + + public void visitJsrStmt(JsrStmt stmt) { + print("jsr "); + + if (stmt.sub() != null) + print(stmt.sub().entry()); + + if (stmt.follow() != null) + print(" ret to " + stmt.follow()); + + println(" caught by " + stmt.catchTargets()); + } + + public void visitSwitchStmt(SwitchStmt stmt) { + print("switch ("); + + if (stmt.index() != null) + stmt.index().visit(this); + + print(")"); + println(" caught by " + stmt.catchTargets()); + + if (stmt.values() != null && stmt.targets() != null) { + for (int i = 0; i < stmt.values().length; i++) { + println(" case " + stmt.values()[i] + ": " + + stmt.targets()[i]); + } + } + + println(" default: " + stmt.defaultTarget()); + } + + public void visitThrowStmt(ThrowStmt stmt) { + print("throw "); + + if (stmt.expr() != null) + stmt.expr().visit(this); + + println(" caught by " + stmt.catchTargets()); + } + + public void visitSCStmt(SCStmt stmt) { + print("aswizzle "); + if (stmt.array() != null) + stmt.array().visit(this); + if (stmt.index() != null) + stmt.index().visit(this); + } + + public void visitSRStmt(SRStmt stmt) { + print("aswrange array: "); + if (stmt.array() != null) + stmt.array().visit(this); + print(" start: "); + if (stmt.start() != null) + stmt.start().visit(this); + print(" end: "); + if (stmt.end() != null) + stmt.end().visit(this); + println(""); + } + + public void visitArithExpr(ArithExpr expr) { + print("("); + + if (expr.left() != null) + expr.left().visit(this); + + print(" "); + + switch (expr.operation()) { + case ArithExpr.ADD: + print("+"); + break; + case ArithExpr.SUB: + print("-"); + break; + case ArithExpr.DIV: + print("/"); + break; + case ArithExpr.MUL: + print("*"); + break; + case ArithExpr.REM: + print("%"); + break; + case ArithExpr.AND: + print("&"); + break; + case ArithExpr.IOR: + print("|"); + break; + case ArithExpr.XOR: + print("^"); + break; + case ArithExpr.CMP: + print("<=>"); + break; + case ArithExpr.CMPL: + print(""); + break; + case ArithExpr.CMPG: + print(""); + break; + } + + print(" "); + if (expr.right() != null) + expr.right().visit(this); + print(")"); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + if (expr.array() != null) + expr.array().visit(this); + print(".length"); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + if (expr.array() != null) + expr.array().visit(this); + print("["); + if (expr.index() != null) + expr.index().visit(this); + print("]"); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + if (expr.receiver() != null) + expr.receiver().visit(this); + + print("."); + if (expr.method() != null) + print(expr.method().nameAndType().name()); + print("("); + + if (expr.params() != null) { + for (int i = 0; i < expr.params().length; i++) { + expr.params()[i].visit(this); + if (i != expr.params().length - 1) { + print(", "); + } + } + } + + print(")"); + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + if (expr.method() != null) + print(expr.method().declaringClass()); + + print("."); + if (expr.method() != null) + print(expr.method().nameAndType().name()); + print("("); + + if (expr.params() != null) { + for (int i = 0; i < expr.params().length; i++) { + expr.params()[i].visit(this); + if (i != expr.params().length - 1) { + print(", "); + } + } + } + + print(")"); + } + + public void visitCastExpr(CastExpr expr) { + print("((" + expr.castType() + ") "); + if (expr.expr() != null) + expr.expr().visit(this); + print(")"); + } + + public void visitConstantExpr(ConstantExpr expr) { + if (expr.value() instanceof String) { + StringBuffer sb = new StringBuffer(); + + String s = (String) expr.value(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (Character.isWhitespace(c) || (0x20 <= c && c <= 0x7e)) { + sb.append(c); + } else { + sb.append("\\u"); + sb.append(Integer.toHexString(c)); + } + + if (sb.length() > 50) { + sb.append("..."); + break; + } + } + + print("'" + sb.toString() + "'"); + } else if (expr.value() instanceof Float) { + print(expr.value() + "F"); + } else if (expr.value() instanceof Long) { + print(expr.value() + "L"); + } else { + print(expr.value()); + } + } + + public void visitFieldExpr(FieldExpr expr) { + if (expr.object() != null) + expr.object().visit(this); + print("."); + if (expr.field() != null) + print(expr.field().nameAndType().name()); + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + if (expr.expr() != null) + expr.expr().visit(this); + print(" instanceof " + expr.checkType()); + } + + public void visitLocalExpr(LocalExpr expr) { + if (expr.fromStack()) { + print("T"); + } else { + print("L"); + } + + print(expr.type().getSignature().toLowerCase()); + print(Integer.toString(expr.index())); + + DefExpr def = expr.def(); + + if (def == null || def.version() == -1) { + print("_undef"); + + } else { + print("_" + def.version()); + } + } + + public void visitNegExpr(NegExpr expr) { + print("-"); + if (expr.expr() != null) + expr.expr().visit(this); + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + print("new " + expr.elementType() + "["); + if (expr.size() != null) + expr.size().visit(this); + print("]"); + } + + public void visitNewExpr(NewExpr expr) { + print("new " + expr.objectType()); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + print("new " + expr.elementType()); + + if (expr.dimensions() != null) { + for (int i = 0; i < expr.dimensions().length; i++) { + print("[" + expr.dimensions()[i] + "]"); + } + } + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + if (expr.expr().type() instanceof ReferenceType) { + print("notNull("); + } else { + print("notZero("); + } + + if (expr.expr() != null) + expr.expr().visit(this); + + print(")"); + } + + public void visitRCExpr(RCExpr expr) { + print("rc("); + if (expr.expr() != null) + expr.expr().visit(this); + print(")"); + } + + public void visitUCExpr(UCExpr expr) { + if (expr.kind() == UCExpr.POINTER) { + print("aupdate("); + } else { + print("supdate("); + } + + if (expr.expr() != null) + expr.expr().visit(this); + print(")"); + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + print("returnAddress"); + } + + public void visitShiftExpr(ShiftExpr expr) { + print("("); + if (expr.expr() != null) + expr.expr().visit(this); + + if (expr.dir() == ShiftExpr.LEFT) { + print("<<"); + } else if (expr.dir() == ShiftExpr.RIGHT) { + print(">>"); + } else if (expr.dir() == ShiftExpr.UNSIGNED_RIGHT) { + print(">>>"); + } + + if (expr.bits() != null) + expr.bits().visit(this); + print(")"); + } + + public void visitStackExpr(StackExpr expr) { + print("S" + expr.type().getSignature().toLowerCase() + expr.index()); + + DefExpr def = expr.def(); + + if (def == null || def.version() == -1) { + print("_undef"); + } else { + print("_" + def.version()); + } + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + if (expr.field() != null) { + print(expr.field().declaringClass() + "." + + expr.field().nameAndType().name()); + } + } + + public void visitExpr(Expr expr) { + print("EXPR"); + } + + public void visitStmt(Stmt stmt) { + print("STMT"); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/RCExpr.java b/src/edu/purdue/cs/bloat/tree/RCExpr.java new file mode 100644 index 0000000..8e75c2f --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/RCExpr.java @@ -0,0 +1,71 @@ +/* + * Class: RCExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * RCExpr represents a residency check. + */ +public class RCExpr extends CheckExpr { + /** + * Constructor. + * + * @param expr + * The expression whose residency is to be checked. + * @param type + * The type of this expression. + */ + public RCExpr(Expr expr, Type type) { + super(expr, type); + } + + public void visit(TreeVisitor visitor) { + visitor.visitRCExpr(this); + } + + public boolean equalsExpr(Expr other) { + return other instanceof RCExpr && super.equalsExpr(other); + } + + public Object clone() { + return copyInto(new RCExpr((Expr) expr.clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ReplaceVisitor.java b/src/edu/purdue/cs/bloat/tree/ReplaceVisitor.java new file mode 100644 index 0000000..acd0617 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ReplaceVisitor.java @@ -0,0 +1,478 @@ +/* + * Class: ReplaceVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * ReplaceVisitor traverses a tree and replaces each occurrence of one Node with + * another Node. + */ +public class ReplaceVisitor extends TreeVisitor { + Node from; + + Node to; + + /** + * Constructor. + * + * @param from + * The "old" Node. + * @param to + * The "new" Node. + */ + public ReplaceVisitor(Node from, Node to) { + this.from = from; + this.to = to; + + if (Tree.DEBUG) { + System.out.println("replace " + from + " VN=" + from.valueNumber() + + " in " + from.parent + " with " + to); + } + } + + public void visitTree(Tree tree) { + if (to instanceof Stmt) { + ((Stmt) to).setParent(tree); + + // The most common statement replacement is the last statement. + // so search from the end of the statement list. + ListIterator iter = tree.stmts.listIterator(tree.stmts.size()); + + while (iter.hasPrevious()) { + Stmt s = (Stmt) iter.previous(); + if (s == from) { + iter.set(to); + break; + } + } + } else { + tree.visitChildren(this); + } + } + + public void visitExprStmt(ExprStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitInitStmt(InitStmt stmt) { + for (int i = 0; i < stmt.targets.length; i++) { + if (stmt.targets[i] == from) { + stmt.targets[i] = (LocalExpr) to; + ((LocalExpr) to).setParent(stmt); + return; + } + } + + stmt.visitChildren(this); + } + + public void visitGotoStmt(GotoStmt stmt) { + stmt.visitChildren(this); + } + + public void visitMonitorStmt(MonitorStmt stmt) { + if (stmt.object == from) { + stmt.object = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitStackManipStmt(StackManipStmt stmt) { + for (int i = 0; i < stmt.target.length; i++) { + if (stmt.target[i] == from) { + stmt.target[i] = (StackExpr) to; + ((Expr) to).setParent(stmt); + return; + } + } + + for (int i = 0; i < stmt.source.length; i++) { + if (stmt.source[i] == from) { + stmt.source[i] = (StackExpr) to; + ((Expr) to).setParent(stmt); + return; + } + } + + stmt.visitChildren(this); + } + + public void visitCatchExpr(CatchExpr expr) { + expr.visitChildren(this); + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + if (stmt.target == from) { + stmt.target = (VarExpr) to; + ((VarExpr) to).setParent(stmt); + } else { + Iterator e = stmt.operands.keySet().iterator(); + + while (e.hasNext()) { + Block block = (Block) e.next(); + + if (stmt.operandAt(block) == from) { + stmt.setOperandAt(block, (Expr) to); + ((Expr) to).setParent(stmt); + return; + } + } + + stmt.visitChildren(this); + } + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + if (stmt.target == from) { + stmt.target = (LocalExpr) to; + ((LocalExpr) to).setParent(stmt); + } else { + ListIterator e = stmt.operands.listIterator(); + + while (e.hasNext()) { + LocalExpr expr = (LocalExpr) e.next(); + + if (expr == from) { + e.set(to); + from.cleanup(); + ((LocalExpr) to).setParent(stmt); + return; + } + } + + stmt.visitChildren(this); + } + } + + public void visitRetStmt(RetStmt stmt) { + stmt.visitChildren(this); + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitReturnStmt(ReturnStmt stmt) { + stmt.visitChildren(this); + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + stmt.visitChildren(this); + } + + public void visitStoreExpr(StoreExpr expr) { + if (expr.target == from) { + expr.target = (MemExpr) to; + ((MemExpr) to).setParent(expr); + } else if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitSwitchStmt(SwitchStmt stmt) { + if (stmt.index == from) { + stmt.index = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitThrowStmt(ThrowStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitSCStmt(SCStmt stmt) { + if (stmt.array == from) { + stmt.array = (Expr) to; + ((Expr) to).setParent(stmt); + } else if (stmt.index == from) { + stmt.index = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitSRStmt(SRStmt stmt) { + if (stmt.array == from) { + stmt.array = (Expr) to; + ((Expr) to).setParent(stmt); + } else if (stmt.start == from) { + stmt.start = (Expr) to; + ((Expr) to).setParent(stmt); + } else if (stmt.end == from) { + stmt.end = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitDefExpr(DefExpr expr) { + expr.visitChildren(this); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + if (expr.array == from) { + expr.array = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitArithExpr(ArithExpr expr) { + if (expr.left == from) { + expr.left = (Expr) to; + ((Expr) to).setParent(expr); + } else if (expr.right == from) { + expr.right = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + if (expr.array == from) { + expr.array = (Expr) to; + ((Expr) to).setParent(expr); + } else if (expr.index == from) { + expr.index = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + if (expr.receiver == from) { + expr.receiver = (Expr) to; + ((Expr) to).setParent(expr); + } else { + for (int i = 0; i < expr.params.length; i++) { + if (expr.params[i] == from) { + expr.params[i] = (Expr) to; + ((Expr) to).setParent(expr); + return; + } + } + + expr.visitChildren(this); + } + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + for (int i = 0; i < expr.params.length; i++) { + if (expr.params[i] == from) { + expr.params[i] = (Expr) to; + ((Expr) to).setParent(expr); + return; + } + } + + expr.visitChildren(this); + } + + public void visitCastExpr(CastExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitConstantExpr(ConstantExpr expr) { + expr.visitChildren(this); + } + + public void visitFieldExpr(FieldExpr expr) { + if (expr.object == from) { + expr.object = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitLocalExpr(LocalExpr expr) { + expr.visitChildren(this); + } + + public void visitNegExpr(NegExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + if (expr.size == from) { + expr.size = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitNewExpr(NewExpr expr) { + expr.visitChildren(this); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + for (int i = 0; i < expr.dimensions.length; i++) { + if (expr.dimensions[i] == from) { + expr.dimensions[i] = (Expr) to; + ((Expr) to).setParent(expr); + return; + } + } + + expr.visitChildren(this); + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + if (stmt.left == from) { + stmt.left = (Expr) to; + ((Expr) to).setParent(stmt); + } else if (stmt.right == from) { + stmt.right = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + expr.visitChildren(this); + } + + public void visitShiftExpr(ShiftExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else if (expr.bits == from) { + expr.bits = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitRCExpr(RCExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitUCExpr(UCExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitStackExpr(StackExpr expr) { + expr.visitChildren(this); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + expr.visitChildren(this); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/RetStmt.java b/src/edu/purdue/cs/bloat/tree/RetStmt.java new file mode 100644 index 0000000..1cdadca --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/RetStmt.java @@ -0,0 +1,79 @@ +/* + * Class: RetStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Subroutine; + +/** + * RetStmt represents the ret opcode which returns from a subroutine. + * Recall that when a subroutine returns, the ret opcode's argument + * specifies a local variable that stores the return address of + */ +public class RetStmt extends JumpStmt { + final Subroutine sub; // Subroutine from which to return. + + /** + * Constructor. + * + * @param sub + * The subroutine in which the return statement resides. That is, + * from where the program control is returning. + * + * @see Tree#addInstruction(Instruction, Subroutine) + */ + public RetStmt(Subroutine sub) { + this.sub = sub; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitRetStmt(this); + } + + public Subroutine sub() { + return sub; + } + + public Object clone() { + return copyInto(new RetStmt(sub)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ReturnAddressExpr.java b/src/edu/purdue/cs/bloat/tree/ReturnAddressExpr.java new file mode 100644 index 0000000..fd4a986 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ReturnAddressExpr.java @@ -0,0 +1,77 @@ +/* + * Class: ReturnAddressExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ReturnAddressExpr represents a return address used with the ret + * opcode. + */ +public class ReturnAddressExpr extends Expr { + /** + * Constructor. + * + * @param type + * The type of this expression (Type.ADDRESS). + */ + public ReturnAddressExpr(Type type) { + super(type); + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitReturnAddressExpr(this); + } + + public int exprHashCode() { + return 18; + } + + public boolean equalsExpr(Expr other) { + return false; + } + + public Object clone() { + return copyInto(new ReturnAddressExpr(type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ReturnExprStmt.java b/src/edu/purdue/cs/bloat/tree/ReturnExprStmt.java new file mode 100644 index 0000000..291ce92 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ReturnExprStmt.java @@ -0,0 +1,76 @@ +/* + * Class: ReturnExprStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * ReturnExprStmt represents the areturn opcode which returns a + * reference from a method. + */ +public class ReturnExprStmt extends JumpStmt { + Expr expr; + + /** + * Constructor. + * + * @param expr + * The expression (reference) returned by this return statement. + */ + public ReturnExprStmt(Expr expr) { + this.expr = expr; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitReturnExprStmt(this); + } + + public Object clone() { + return copyInto(new ReturnExprStmt((Expr) expr.clone())); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ReturnStmt.java b/src/edu/purdue/cs/bloat/tree/ReturnStmt.java new file mode 100644 index 0000000..2bc5ddd --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ReturnStmt.java @@ -0,0 +1,65 @@ +/* + * Class: ReturnStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * ReturnStmt represents the return opcode which returns + * void from a method. + */ +public class ReturnStmt extends JumpStmt { + // return + + /** + * Constructor. + */ + public ReturnStmt() { + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitReturnStmt(this); + } + + public Object clone() { + return copyInto(new ReturnStmt()); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/SCStmt.java b/src/edu/purdue/cs/bloat/tree/SCStmt.java new file mode 100644 index 0000000..38d7e94 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/SCStmt.java @@ -0,0 +1,106 @@ +/* + * Class: SCStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * SCStmt represents a swizzle check on an element in an array. + */ +public class SCStmt extends Stmt { + + Expr array; + + Expr index; + + boolean redundant; + + /** + * Constructor. + * + * @param a + * The array on which to place the swizzle check. + * @param i + * The element in array a to swizzle. + */ + public SCStmt(Expr a, Expr i) { + this.array = a; + this.index = i; + this.redundant = false; + array.setParent(this); + index.setParent(this); + } + + public Expr array() { + return array; + } + + public Expr index() { + return index; + } + + /** + * @return True, if the swizzle check is redundent. + */ + public boolean redundant() { + return redundant; + } + + public void set_redundant(boolean val) { + this.redundant = val; + } + + public void visit(TreeVisitor visitor) { + visitor.visitSCStmt(this); + } + + public Object clone() { + return copyInto(new SCStmt((Expr) array.clone(), (Expr) index.clone())); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + index.visit(visitor); + array.visit(visitor); + } else { + array.visit(visitor); + index.visit(visitor); + } + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/SRStmt.java b/src/edu/purdue/cs/bloat/tree/SRStmt.java new file mode 100644 index 0000000..0b7999e --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/SRStmt.java @@ -0,0 +1,103 @@ +/* + * Class: SRStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * SRStmt represents the swizzle of a range of elements in an array. + */ +public class SRStmt extends Stmt { + Expr array; + + Expr start; // starting value of range + + Expr end; // terminating value of range + + /** + * Constructor. + * + * @param a + * The array to swizzle. + * @param s + * The starting value of the swizzle range. + * @param t + * The terminating value of the swizzle range. + */ + public SRStmt(Expr a, Expr s, Expr t) { + this.array = a; + this.start = s; + this.end = t; + array.setParent(this); + start.setParent(this); + end.setParent(this); + } + + public Expr array() { + return array; + } + + public Expr start() { + return start; + } + + public Expr end() { + return end; + } + + public void visit(TreeVisitor visitor) { + visitor.visitSRStmt(this); + } + + public Object clone() { + return copyInto(new SRStmt((Expr) array.clone(), (Expr) start.clone(), + (Expr) end.clone())); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + end.visit(visitor); + start.visit(visitor); + array.visit(visitor); + } else { + array.visit(visitor); + start.visit(visitor); + end.visit(visitor); + } + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ShiftExpr.java b/src/edu/purdue/cs/bloat/tree/ShiftExpr.java new file mode 100644 index 0000000..58d56e7 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ShiftExpr.java @@ -0,0 +1,124 @@ +/* + * Class: ShiftExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ShiftExpr represents a bit shift operation. + */ +public class ShiftExpr extends Expr { + final int dir; + + Expr expr; + + Expr bits; + + public static final int LEFT = 0; + + public static final int RIGHT = 1; + + public static final int UNSIGNED_RIGHT = 2; + + /** + * Constructor. + * + * @param dir + * The direction (LEFT, RIGHT, or UNSIGNED_RIGHT) in which to + * shift. + * @param expr + * The expression to shift. + * @param bits + * The number of bits to shift. + * @param type + * The type of this expression. + */ + public ShiftExpr(int dir, Expr expr, Expr bits, Type type) { + super(type); + this.dir = dir; + this.expr = expr; + this.bits = bits; + + expr.setParent(this); + bits.setParent(this); + } + + public int dir() { + return dir; + } + + public Expr expr() { + return expr; + } + + public Expr bits() { + return bits; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + bits.visit(visitor); + expr.visit(visitor); + } else { + expr.visit(visitor); + bits.visit(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitShiftExpr(this); + } + + public int exprHashCode() { + return 19 + dir ^ expr.exprHashCode() ^ bits.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof ShiftExpr + && ((ShiftExpr) other).dir == dir + && ((ShiftExpr) other).expr.equalsExpr(expr) + && ((ShiftExpr) other).bits.equalsExpr(bits); + } + + public Object clone() { + return copyInto(new ShiftExpr(dir, (Expr) expr.clone(), (Expr) bits + .clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/StackExpr.java b/src/edu/purdue/cs/bloat/tree/StackExpr.java new file mode 100644 index 0000000..ba5b676 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StackExpr.java @@ -0,0 +1,84 @@ +/* + * Class: StackExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.util.Assert; + +/** + * StackExpr represents an expression that is stored on the stack. + */ +public class StackExpr extends VarExpr { + /** + * Constructor. + * + * @param index + * Location (offset) in stack of the information to which the + * expression refers. Index 0 represents the bottom of the stack. + * @param type + * The type of this expression. + */ + public StackExpr(int index, Type type) { + super(index, type); + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitStackExpr(this); + } + + public int exprHashCode() { + return 20 + index + (int) Assert.simple(type).hashCode(); + } + + public boolean equalsExpr(Expr other) { + return other instanceof StackExpr + && Assert.simple(((StackExpr) other).type).equals( + Assert.simple(type)) + && ((StackExpr) other).index == index; + } + + public Object clone() { + return copyInto(new StackExpr(index, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/StackManipStmt.java b/src/edu/purdue/cs/bloat/tree/StackManipStmt.java new file mode 100644 index 0000000..caca2f4 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StackManipStmt.java @@ -0,0 +1,156 @@ +/* + * Class: StackManipStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * StackManipStmt represents the opcodes that manipulate the stack such as + * swap and dup. + */ +public class StackManipStmt extends Stmt implements Assign { + StackExpr[] target; + + StackExpr[] source; + + int kind; + + // 0 1 -> 1 0 + public static final int SWAP = 0; + + // 0 -> 0 0 + public static final int DUP = 1; + + // 0 1 -> 1 0 1 + public static final int DUP_X1 = 2; + + // 0 1 2 -> 2 0 1 2 + public static final int DUP_X2 = 3; + + // 0 1 -> 0 1 0 1 + public static final int DUP2 = 4; + + // 0 1 2 -> 1 2 0 1 2 + public static final int DUP2_X1 = 5; + + // 0 1 2 3 -> 2 3 0 1 2 3 + public static final int DUP2_X2 = 6; + + /** + * Constructor. + * + * @param target + * The new contents of the stack + * @param source + * The old contents of the stack + * @param kind + * The kind of stack manipulation (SWAP, DUP, etc.) to take + * place. + */ + public StackManipStmt(StackExpr[] target, StackExpr[] source, int kind) { + this.kind = kind; + + this.target = target; + + for (int i = 0; i < target.length; i++) { + this.target[i].setParent(this); + } + + this.source = source; + + for (int i = 0; i < source.length; i++) { + this.source[i].setParent(this); + } + } + + public DefExpr[] defs() { + return target; + } + + public StackExpr[] target() { + return target; + } + + public StackExpr[] source() { + return source; + } + + public int kind() { + return kind; + } + + public void visit(TreeVisitor visitor) { + visitor.visitStackManipStmt(this); + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + for (int i = target.length - 1; i >= 0; i--) { + target[i].visit(visitor); + } + + for (int i = source.length - 1; i >= 0; i--) { + source[i].visit(visitor); + } + } else { + for (int i = 0; i < source.length; i++) { + source[i].visit(visitor); + } + + for (int i = 0; i < target.length; i++) { + target[i].visit(visitor); + } + } + } + + public Object clone() { + StackExpr[] t = new StackExpr[target.length]; + + for (int i = 0; i < target.length; i++) { + t[i] = (StackExpr) target[i].clone(); + } + + StackExpr[] s = new StackExpr[source.length]; + + for (int i = 0; i < source.length; i++) { + s[i] = (StackExpr) source[i].clone(); + } + + return copyInto(new StackManipStmt(t, s, kind)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/StackOptimizationData.java b/src/edu/purdue/cs/bloat/tree/StackOptimizationData.java new file mode 100644 index 0000000..2994e70 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StackOptimizationData.java @@ -0,0 +1,354 @@ +/* + * Class: StackOptimizationData + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.util.Assert; + +/** + * StackOptimizationData analyzes the relative distances of various uses of the + * same definition of a local variable to add dups and swaps to the bytecode and + * eliminate loads and stores. + * + * @author Thomas VanDrunen + */ + +public class StackOptimizationData { + static boolean DEBUG = false; + + HashMap defInfoMap; /* + * maps LocalExprs (which are definitions) to + * DefInformations + */ + + HashMap useInfoMap; /* maps LocalExprs to UseInformations */ + + Block owningBlock; + + public StackOptimizationData(Block owningBlock) { + this.owningBlock = owningBlock; + defInfoMap = new LinkedHashMap(); + useInfoMap = new LinkedHashMap(); + } + + /** + * Optimize runs the algorithm for analyzing the tree, looking for + * opportunities to replaces stores and loads with dups and swaps. It + * initiates several visitors, and information is sotred in defInfoMap and + * useInfoMap + */ + + public void optimize() { + // Get all the LocalExprs in the block + Vector LEs = (new LEGatherer()).getLEs(owningBlock); + LocalExpr current; + + for (int i = 0; i < LEs.size(); i++) { + + current = (LocalExpr) LEs.elementAt(i); + + useInfoMap.put(current, new UseInformation()); + + if (current.isDef()) { + DefInformation DI = new DefInformation(current.uses.size()); + defInfoMap.put(current, DI); + // send the DefInformation the number of uses of the var + } else if (current.def() != null) { + + DefInformation DI = (DefInformation) defInfoMap.get(current + .def()); + + if (DI == null) + continue; // if it has no def information, + // it's probably a parameter, and we need to store it + // anyway. + + DI.usesFound++; + + // handle a special case + // if we have something like L := L + k, we can do + // "iinc L, k", which would be better (wouldn't it?) than + // propogating L on the stack, loading a constant, adding, + // and saving. In that case, also, we'll have to load. + // (see codegen/CodeGenerator.java, circa line 1334) + + if (current.parent() instanceof ArithExpr + && current.parent().parent() instanceof StoreExpr + && ((((ArithExpr) current.parent()).left() instanceof ConstantExpr && Assert + .isIntegral(((ArithExpr) current.parent()) + .left().type())) || (((ArithExpr) current + .parent()).right() instanceof ConstantExpr && Assert + .isIntegral(((ArithExpr) current.parent()) + .left().type()))) + && (((StoreExpr) current.parent().parent()).target() instanceof LocalExpr) + && (((LocalExpr) ((StoreExpr) current.parent().parent()) + .target()).index() == current.index())) + + DI.type1s += 3; + + // another special case. If this use is rhs of a store + // and the lhs has the same index, the store will be + // eliminated. DO NOT count this as type 0 or type 1. + // ... + // OOPS-- exception to that (I am NOT having fun right now): + // If the StoreExpr is not the child of an ExprStmt, + // sure, the store will be eliminated, BUT THE LOAD won't be. + // So, we need to go ahead and search afterall; hence + // the second condition + + else if (current.parent() instanceof StoreExpr + && current.parent().parent() instanceof ExprStmt + && (((StoreExpr) current.parent()).target() instanceof LocalExpr) + && (((LocalExpr) ((StoreExpr) current.parent()) + .target()).index() == current.index())) { + DI.type1s += 3; // the new "definition" no doubt + // has uses, so we need the original stored + continue; + } + + else { + + // first search using a Type0Visitor. If that search + // fails, use a Type1Visitor. (The second condition + // checks + // whether we have too many type 1s already, and will + // have to store it.... there's no point in looking for + // anymore + if (!((new Type0Visitor(defInfoMap, useInfoMap)) + .search(current)) + && DI.type1s < 3) { + + // Java, I hate you as much as I love you. + // I blink my eyes and more complications spring up. + // So far I have been happily ignoring the problem + // of wide expressions-- there's no way we can + // do type 1 optimizations on wide values because + // we can't do a swap on values that take up two + // stack positions... + + if (current.type().getSize() == 2) + DI.type1s += 3; // give up + else + (new Type1Visitor(defInfoMap, useInfoMap)) + .search(current); + } + } + } + } + } + + /** + * Various methods used by CodeGenerator, used as an interface into the + * information in defInfoMap and useInfoMap + */ + + public boolean shouldStore(LocalExpr expr) { + + // We should store if there are more than 2 type 1 uses or + // any uses of type greater than one-- which will be indicated + // by type1s being greater than 2 + + // the parameter expr might be null, e.g., if this method is + // called from "dups" in "!shouldStore((LocalExpr) expr.def())", + // because if the expression is a use of a parameter in a method, + // its definition is null. Return true in that case because it + // will be saved to a local anyway + if (expr == null) + return true; + + DefInformation DI = (DefInformation) defInfoMap.get(expr); + if (DI == null) { + if (DEBUG) { + System.err + .println("Error in StackOptimizationData.shouldStore: parameter not found in defInfoMap:"); + System.err.println(expr.toString()); + } + + return true; + } + + if (DI.type1s > 2 || DI.usesFound < DI.uses) + return true; + else + return false; + } + + public int dups(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizationData.dups: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += (UI.type0s - UI.type0_x1s - UI.type0_x2s); + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += (UI.type1s - UI.type1_x1s - UI.type1_x2s); + + return toReturn; + } + + public int dup_x1s(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizationData.dup_x1s: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += UI.type0_x1s; + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += UI.type1_x1s; + + return toReturn; + } + + public int dup_x2s(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizationData.dup_x2s: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += UI.type0_x2s; + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += UI.type1_x2s; + + return toReturn; + } + + public boolean onStack(LocalExpr expr) { + + if (expr.isDef()) + return false; + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizationData.onStack: parameter not found in useInfoMap"); + return false; + } + + if (UI.type == 0) + return true; + + if (UI.type == 1 && !shouldStore((LocalExpr) expr.def())) + return true; + + return false; + } + + public boolean shouldSwap(LocalExpr expr) { + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizationData.onStack: parameter not found in useInfoMap"); + return false; + } + + return (onStack(expr) && UI.type == 1); + } + + public void infoDisplay(LocalExpr expr) { + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + DefInformation DI = (DefInformation) defInfoMap.get(expr); + + System.err.println(expr.toString()); + System.err.println(expr.parent().toString() + "-" + + expr.parent().parent().toString()); + if (expr.parent().parent().parent() != null + && expr.parent().parent().parent().parent() != null) + System.err.println(expr.parent().parent().parent().toString() + "-" + + expr.parent().parent().parent().parent().toString()); + + if (DI == null) { + System.err.println("not a definition"); + if (expr.def() == null) + System.err.println("has no definition (is parameter?)"); + else + System.err.println("has definition " + expr.def()); + } else { + System.err.println("a definition with " + DI.type1s + + " type1s total"); + System.err.println("uses: " + DI.uses); + System.err.println("uses found: " + DI.usesFound); + if (shouldStore(expr)) + System.err.println("should store"); + } + + if (UI == null) + System.err.println("No use information entry. trouble"); + else { + if (DI == null) + System.err.println("type on stack: " + UI.type); + System.err.println("type0s for this instance: " + UI.type0s); + System.err.println("of above, number of x1s: " + UI.type0_x1s); + System.err.println("of above, number of x2s: " + UI.type0_x2s); + System.err.println("type1s for this instance: " + UI.type1s); + System.err.println("of above, number of x1s: " + UI.type1_x1s); + System.err.println("of above, number of x2s: " + UI.type1_x2s); + } + + } +} diff --git a/src/edu/purdue/cs/bloat/tree/StackOptimizer.java b/src/edu/purdue/cs/bloat/tree/StackOptimizer.java new file mode 100644 index 0000000..bef2dab --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StackOptimizer.java @@ -0,0 +1,388 @@ +/* + * Class: StackOptimizer + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; +import edu.purdue.cs.bloat.optimize.MethodState; +import edu.purdue.cs.bloat.optimize.Optimization; +import edu.purdue.cs.bloat.util.Assert; + +/** + * StackOptimizer analyzes the relative distances of various uses of the same + * definition of a local variable to add dups and swaps to the bytecode and + * eliminate loads and stores. + * + * @author Thomas VanDrunen + */ + +public class StackOptimizer implements Optimization { + static boolean DEBUG = false; + + LinkedHashMap defInfoMap; /* + * maps LocalExprs (which are definitions) to + * DefInformations + */ + + LinkedHashMap useInfoMap; /* maps LocalExprs to UseInformations */ + + Block owningBlock; + + public StackOptimizer(Block owningBlock) { + this.owningBlock = owningBlock; + defInfoMap = new LinkedHashMap(); + useInfoMap = new LinkedHashMap(); + } + + public static void optimizeCFG(FlowGraph cfg) { + List blocks = cfg.preOrder(); + for (Iterator it = blocks.iterator(); it.hasNext();) + ((Block) it.next()).stackOptimizer().optimize(); + } + + // Essentially here to complete the Optimization interface. May be + // usable in the future. + public void transform(MethodState state) { + List blocks = state.controlFlowGraph().preOrder(); + + for (Iterator i = blocks.iterator(); i.hasNext();) + ((Block) i.next()).stackOptimizer().optimize(); + } + + /** + * Optimize runs the algorithm for analyzing the tree, looking for + * opportunities to replaces stores and loads with dups and swaps. It + * initiates several visitors, and information is sotred in defInfoMap and + * useInfoMap + */ + + public void optimize() { + + Vector LEs = (new LEGatherer()).getLEs(owningBlock); // get all the + LocalExpr current; // LocalExprs in the block + + for (int i = 0; i < LEs.size(); i++) { + + current = (LocalExpr) LEs.elementAt(i); + + useInfoMap.put(current, new UseInformation()); + + if (current.isDef()) { + DefInformation DI = new DefInformation(current.uses.size()); + defInfoMap.put(current, DI); + // send the DefInformation the number of uses of the var + } else if (current.def() != null) { + + DefInformation DI = (DefInformation) defInfoMap.get(current + .def()); + + if (DI == null) + continue; // if it has no def information, + // it's probably a parameter, and we need to store it + // anyway. + + DI.usesFound++; + + // handle a special case + // if we have something like L := L + k, we can do + // "iinc L, k", which would be better (wouldn't it?) than + // propogating L on the stack, loading a constant, adding, + // and saving. In that case, also, we'll have to load. + // (see codegen/CodeGenerator.java, circa line 1334) + + if (current.parent() instanceof ArithExpr + && current.parent().parent() instanceof StoreExpr + && ((((ArithExpr) current.parent()).left() instanceof ConstantExpr && Assert + .isIntegral(((ArithExpr) current.parent()) + .left().type())) || (((ArithExpr) current + .parent()).right() instanceof ConstantExpr && Assert + .isIntegral(((ArithExpr) current.parent()) + .left().type()))) + && (((StoreExpr) current.parent().parent()).target() instanceof LocalExpr) + && (((LocalExpr) ((StoreExpr) current.parent().parent()) + .target()).index() == current.index())) + + DI.type1s += 3; + + // another special case. If this use is rhs of a store + // and the lhs has the same index, the store will be + // eliminated. DO NOT count this as type 0 or type 1. + // ... + // OOPS-- exception to that (I am NOT having fun right now): + // If the StoreExpr is not the child of an ExprStmt, + // sure, the store will be eliminated, BUT THE LOAD won't be. + // So, we need to go ahead and search afterall; hence + // the second condition + + else if (current.parent() instanceof StoreExpr + && current.parent().parent() instanceof ExprStmt + && (((StoreExpr) current.parent()).target() instanceof LocalExpr) + && (((LocalExpr) ((StoreExpr) current.parent()) + .target()).index() == current.index())) { + DI.type1s += 3; // the new "definition" no doubt + // has uses, so we need the original stored + continue; + } + + else { + + // first search using a Type0Visitor. If that search + // fails, use a Type1Visitor. (The second condition + // checks + // whether we have too many type 1s already, and will + // have to store it.... there's no point in looking for + // anymore + if (!((new Type0Visitor(defInfoMap, useInfoMap)) + .search(current)) + && DI.type1s < 3) { + + // Java, I hate you as much as I love you. + // I blink my eyes and more complications spring up. + // So far I have been happily ignoring the problem + // of wide expressions-- there's no way we can + // do type 1 optimizations on wide values because + // we can't do a swap on values that take up two + // stack positions... + + if (current.type().getSize() == 2) + DI.type1s += 3; // give up + else + (new Type1Visitor(defInfoMap, useInfoMap)) + .search(current); + } + } + } + } + } + + /** + * Various methods used by CodeGenerator, used as an interface into the + * information in defInfoMap and useInfoMap + */ + + public boolean shouldStore(LocalExpr expr) { + + // We should store if there are more than 2 type 1 uses or + // any uses of type greater than one-- which will be indicated + // by type1s being greater than 2 + + // the parameter expr might be null, e.g., if this method is + // called from "dups" in "!shouldStore((LocalExpr) expr.def())", + // because if the expression is a use of a parameter in a method, + // its definition is null. Return true in that case because it + // will be saved to a local anyway + if (expr == null) + return true; + + DefInformation DI = (DefInformation) defInfoMap.get(expr); + if (DI == null) { + if (DEBUG) { + System.err + .println("Error in StackOptimizer.shouldStore: parameter not found in defInfoMap:"); + System.err.println(expr.toString()); + } + + return true; + } + + if (DI.type1s > 2 || DI.usesFound < DI.uses) + return true; + else + return false; + } + + public int dups(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizer.dups: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += (UI.type0s - UI.type0_x1s - UI.type0_x2s); + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += (UI.type1s - UI.type1_x1s - UI.type1_x2s); + + return toReturn; + } + + public int dup_x1s(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizer.dup_x1s: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += UI.type0_x1s; + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += UI.type1_x1s; + + return toReturn; + } + + public int dup_x2s(LocalExpr expr) { + + int toReturn = 0; + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizer.dup_x2s: parameter not found in useInfoMap"); + return toReturn; + } + + toReturn += UI.type0_x2s; + if ((expr.isDef() && !shouldStore(expr)) + || (!(expr.isDef()) && !shouldStore((LocalExpr) expr.def()))) + toReturn += UI.type1_x2s; + + return toReturn; + } + + public boolean onStack(LocalExpr expr) { + + if (expr.isDef()) + return false; + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizer.onStack: parameter not found in useInfoMap"); + return false; + } + + if (UI.type == 0) + return true; + + if (UI.type == 1 && !shouldStore((LocalExpr) expr.def())) + return true; + + return false; + } + + public boolean shouldSwap(LocalExpr expr) { + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (DEBUG) + System.err + .println("Error in StackOptimizer.onStack: parameter not found in useInfoMap"); + return false; + } + + return (onStack(expr) && UI.type == 1); + } + + public void infoDisplay(LocalExpr expr) { + + UseInformation UI = (UseInformation) useInfoMap.get(expr); + DefInformation DI = (DefInformation) defInfoMap.get(expr); + + System.err.println(expr.toString()); + System.err.println(expr.parent().toString() + "-" + + expr.parent().parent().toString()); + if (expr.parent().parent().parent() != null + && expr.parent().parent().parent().parent() != null) + System.err.println(expr.parent().parent().parent().toString() + "-" + + expr.parent().parent().parent().parent().toString()); + + if (DI == null) { + System.err.println("not a definition"); + if (expr.def() == null) + System.err.println("has no definition (is parameter?)"); + else + System.err.println("has definition " + expr.def()); + } else { + System.err.println("a definition with " + DI.type1s + + " type1s total"); + System.err.println("uses: " + DI.uses); + System.err.println("uses found: " + DI.usesFound); + if (shouldStore(expr)) + System.err.println("should store"); + } + + if (UI == null) + System.err.println("No use information entry. trouble"); + else { + if (DI == null) + System.err.println("type on stack: " + UI.type); + System.err.println("type0s for this instance: " + UI.type0s); + System.err.println("of above, number of x1s: " + UI.type0_x1s); + System.err.println("of above, number of x2s: " + UI.type0_x2s); + System.err.println("type1s for this instance: " + UI.type1s); + System.err.println("of above, number of x1s: " + UI.type1_x1s); + System.err.println("of above, number of x2s: " + UI.type1_x2s); + } + + } + + public String traceMessage(String dateString) { + return straceMessage(dateString); + } + + public static String straceMessage(String dateString) { + return " Old stack optimization: " + dateString; + } + + public String preDebugMessage() { + return null; + } + + public String postDebugMessage() { + return null; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/StaticFieldExpr.java b/src/edu/purdue/cs/bloat/tree/StaticFieldExpr.java new file mode 100644 index 0000000..dac6fb5 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StaticFieldExpr.java @@ -0,0 +1,92 @@ +/* + * Class: StaticFieldExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.util.Assert; + +/** + * StaticFieldExpr represents the getstatic opcode which gets a + * static (class) field from a class. + */ +public class StaticFieldExpr extends MemRefExpr { + // getstatic + + final MemberRef field; + + /** + * Constructor. + * + * @param field + * The field to access. + * @param type + * The type of this expression. + */ + public StaticFieldExpr(MemberRef field, Type type) { + super(type); + this.field = field; + } + + public MemberRef field() { + return field; + } + + public void visitForceChildren(TreeVisitor visitor) { + } + + public void visit(TreeVisitor visitor) { + visitor.visitStaticFieldExpr(this); + } + + public int exprHashCode() { + return 21 + field.hashCode() ^ (int) Assert.simple(type).hashCode(); + } + + public boolean equalsExpr(Expr other) { + return other != null && other instanceof StaticFieldExpr + && ((StaticFieldExpr) other).field.equals(field); + } + + public Object clone() { + return copyInto(new StaticFieldExpr(field, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Stmt.java b/src/edu/purdue/cs/bloat/tree/Stmt.java new file mode 100644 index 0000000..dc93a1e --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Stmt.java @@ -0,0 +1,56 @@ +/* + * Class: Stmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * Stmt is a super class used to represent statements in a Java bytecode + * program. As opposed to expressions, statements cannot be nested. + * + * @see JumpStmt + * @see ExprStmt + * @see PhiStmt + * + */ +public abstract class Stmt extends Node { + public void cleanupOnly() { + } + + public abstract Object clone(); +} diff --git a/src/edu/purdue/cs/bloat/tree/StoreExpr.java b/src/edu/purdue/cs/bloat/tree/StoreExpr.java new file mode 100644 index 0000000..561b91a --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/StoreExpr.java @@ -0,0 +1,127 @@ +/* + * Class: StoreExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * StoreExpr represents a store of an expression into a memory location. + * + * @see MemExpr + */ +public class StoreExpr extends Expr implements Assign { + MemExpr target; + + Expr expr; + + /** + * Constructor. + * + * @param target + * The memory location (or local variable, etc.) into which expr + * is stored. + * @param expr + * An expression whose value is to be stored. + * @param type + * The type of this expression. + */ + public StoreExpr(MemExpr target, Expr expr, Type type) { + super(type); + + this.target = target; + this.expr = expr; + + target.setParent(this); + expr.setParent(this); + } + + /** + * Returns the MemExpr into which the expression is stored. + */ + public DefExpr[] defs() { + return new DefExpr[] { target }; + } + + /** + * Returns the memory location (or local variable) into which the expression + * is stored. + */ + public MemExpr target() { + return target; + } + + /** + * Returns the expression being stored. + */ + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + if (visitor.reverse()) { + target.visitOnly(visitor); + expr.visit(visitor); + target.visitChildren(visitor); + } else { + target.visitChildren(visitor); + expr.visit(visitor); + target.visitOnly(visitor); + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitStoreExpr(this); + } + + public int exprHashCode() { + return 22 + target.exprHashCode() ^ expr.exprHashCode(); + } + + public boolean equalsExpr(Expr other) { + return other instanceof StoreExpr + && ((StoreExpr) other).target.equalsExpr(target) + && ((StoreExpr) other).expr.equalsExpr(expr); + } + + public Object clone() { + return copyInto(new StoreExpr((MemExpr) target.clone(), (Expr) expr + .clone(), type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/SwitchStmt.java b/src/edu/purdue/cs/bloat/tree/SwitchStmt.java new file mode 100644 index 0000000..8b38916 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/SwitchStmt.java @@ -0,0 +1,117 @@ +/* + * Class: SwitchStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * SwitchStmt represents a switch statement. + */ +public class SwitchStmt extends JumpStmt { + Expr index; + + Block defaultTarget; + + final Block[] targets; + + final int[] values; + + /** + * Constructor. + * + * @param index + * The expression on which the switch is made. + * @param defaultTarget + * The code to be executed if index is not contained in values. + * @param targets + * The code to be executed for each value in values. + * @param values + * The interesting values that index can have. That is, the + * values of index in which a non-default target is executed. + */ + public SwitchStmt(Expr index, Block defaultTarget, Block[] targets, + int[] values) { + this.index = index; + this.defaultTarget = defaultTarget; + this.targets = targets; + this.values = values; + + index.setParent(this); + } + + public Expr index() { + return index; + } + + public void setDefaultTarget(Block block) { + this.defaultTarget = block; + } + + public Block defaultTarget() { + return defaultTarget; + } + + public Block[] targets() { + return targets; + } + + public int[] values() { + return values; + } + + public void visitForceChildren(TreeVisitor visitor) { + index.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitSwitchStmt(this); + } + + public Object clone() { + Block[] t = new Block[targets.length]; + System.arraycopy(targets, 0, t, 0, targets.length); + + int[] v = new int[values.length]; + System.arraycopy(values, 0, v, 0, values.length); + + return copyInto(new SwitchStmt((Expr) index.clone(), defaultTarget, t, + v)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Swizzler.java b/src/edu/purdue/cs/bloat/tree/Swizzler.java new file mode 100644 index 0000000..ed1932c --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Swizzler.java @@ -0,0 +1,142 @@ +/* + * Class: Swizzler + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; + +/** + * Swizzler represents an induction variable that is used as an index into an + * array. Analysis can be done to determine if array swizzle (aswizzle) + * instruction(s) can be hoisted out of the loop. + * + * @see edu.purdue.cs.bloat.diva.InductionVarAnalyzer InductionVarAnalyzer + */ +public class Swizzler { + Expr ind_var; // induction variable (iv) + + Expr target; // target of the phi defining the ind_var + + Expr init_val; // initial value of the iv + + Expr end_val; // terminating value of the iv + + Expr array; // arrayref which uses the iv as the index + + Block phi_block; // block of the phi defining the ind_var + + SCStmt aswizzle; // the aswizzle stmt that could be removed + + /** + * Constructor. + * + * @param var + * Induction variable. (An index variable for an array.) + * @param tgt + * Target of the phi statement that defines the induction + * variable. + * @param val + * Initial value of the induction variable. + * @param phiblock + * The block in which the phi statement resides. + */ + public Swizzler(Expr var, Expr tgt, Expr val, Block phiblock) { + this.ind_var = var; + this.target = tgt; + this.init_val = val; + this.end_val = null; + this.array = null; + this.phi_block = phiblock; + this.aswizzle = null; + } + + /** + * Sets the ending value for the induction variable. + * + * @param end + * The final value the induction variable will take on. + */ + public void set_end_val(Expr end) { + this.end_val = end; + } + + /** + * @param a + * The array that is indexed by the induction variable. + */ + public void set_array(Expr a) { + this.array = a; + } + + /** + * @param sc + * The aswizzle statement that could be removed from the block. + */ + public void set_aswizzle(SCStmt sc) { + this.aswizzle = sc; + } + + public Expr ind_var() { + return ind_var; + } + + public Expr target() { + return target; + } + + public Expr init_val() { + return init_val; + } + + public Expr end_val() { + return end_val; + } + + public Expr array() { + return array; + } + + public Block phi_block() { + return phi_block; + } + + public SCStmt aswizzle() { + return aswizzle; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ThrowStmt.java b/src/edu/purdue/cs/bloat/tree/ThrowStmt.java new file mode 100644 index 0000000..44102af --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ThrowStmt.java @@ -0,0 +1,76 @@ +/* + * Class: ThrowStmt + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * ThrowStmt represents the athrow opcode which throws an exception + * or error. + */ +public class ThrowStmt extends JumpStmt { + Expr expr; + + /** + * Constructor. + * + * @param expr + * The exception to be thrown. + */ + public ThrowStmt(Expr expr) { + this.expr = expr; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(TreeVisitor visitor) { + visitor.visitThrowStmt(this); + } + + public Object clone() { + return copyInto(new ThrowStmt((Expr) expr.clone())); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Tree.java b/src/edu/purdue/cs/bloat/tree/Tree.java new file mode 100644 index 0000000..859ba32 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Tree.java @@ -0,0 +1,3257 @@ +/* + * Class: Tree + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; +import org.apache.bcel.classfile.*; +import org.apache.bcel.generic.*; +import org.apache.bcel.classfile.ClassFormatException; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.Subroutine; +import edu.purdue.cs.bloat.editor.InstructionVisitor; +import edu.purdue.cs.bloat.editor.MemberRef; +import edu.purdue.cs.bloat.editor.MethodRef; +import edu.purdue.cs.bloat.editor.NameAndType; +import edu.purdue.cs.bloat.util.Assert; + +/** + * Tree represents the expression tree of a basic Block. It consists of an + * operand (expression) stack comprised of expressions and a list of statements + * contained in the block. + * + * @see Block + * @see Expr + * @see OperandStack + * @see Stmt see StmtList + */ +public class Tree extends Node implements InstructionVisitor, + org.apache.bcel.generic.Visitor { + public static boolean DEBUG = false; + + public static boolean FLATTEN = false; + + public static boolean USE_STACK = false; // Do we use stack vars? + + public static boolean AUPDATE_FIX_HACK = false; + + public static boolean AUPDATE_FIX_HACK_CHANGED = false; + + public static boolean USE_PERSISTENT = false; // Insert UCExpr by + + // default + + Block block; // Block that is represented by this Tree + + Subroutine sub; // The Subroutine that we're currently in + + Block next; // The Block after this one + + OperandStack stack; // The program stack for this block + + StmtList stmts; // The statements in the basic block + + Stack savedStack; + + static int stackpos = 0; + + boolean saveValue; // Do we push a StoreExpr on operand stack? + + // Some dup instruction combinations cause saveStack to generate + // temporaries that clobber other temporaries. So, we use nextindex + // to ensure that a uniquely-name temporary is created by + // saveStack. Yes, this will introduce a lot of new temporaries, + // but expression propagation should eliminate a lot of them. + private int nextIndex = 0; + + private void db(String s) { + if (DEBUG) + System.out.println(s); + } + + /** + * Constructor. + * + * @param block + * The basic Block of code represented in this Tree. + * @param predStack + * The contents of the operand stack from the previous basic + * Block. + */ + public Tree(Block block, OperandStack predStack) { + this.block = block; + + if (Tree.DEBUG) { + System.out.println(" new tree for " + block); + } + + stack = new OperandStack(); + + stmts = new StmtList(); + + // The first statement in the Tree is the label indicating the start + // of the basic Block. + appendStmt(new LabelStmt(block.label())); + + // Make a copy of predStack + for (int i = 0; i < predStack.size(); i++) { + Expr expr = (Expr) predStack.get(i); + Expr copy = (Expr) expr.clone(); + copy.setDef(null); + stack.push(copy); + } + } + + /** + * Cleans up this node. Does nothing in this case. + */ + public void cleanupOnly() { + } + + /** + * Add a Collection of local variables to the block. Add an InitStmt to the + * statement list. + * + * @see LocalExpr + * @see InitStmt + */ + public void initLocals(Collection locals) { + LocalExpr[] t = new LocalExpr[locals.size()]; + + if (t.length == 0) { + return; + } + + Iterator iter = locals.iterator(); + + for (int i = 0; iter.hasNext(); i++) { + t[i] = (LocalExpr) iter.next(); + } + + addStmt(new InitStmt(t)); + } + + /** + * Removes a statement from the statement list. + * + * @param stmt + * The statement to remove + */ + public void removeStmt(Stmt stmt) { + stmts.remove(stmt); + } + + /** + * Removes the last non-Label statement from the statement list. + */ + public void removeLastStmt() { + ListIterator iter = stmts.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + Stmt s = (Stmt) iter.previous(); + + if (s instanceof LabelStmt) { + continue; + } + + iter.remove(); + return; + } + } + + /** + * @return The statement list + */ + public List stmts() { + return stmts; + } + + /** + * StmtList is a linked list of statements. A number of methods are + * overridden because some adjustments may need to be made to the Nodes in + * the tree when certain operations are performed. + */ + class StmtList extends LinkedList { + static final long serialVersionUID = 1L; + + /** + * Clear the contents of this statement list. + */ + public void clear() { + Iterator iter = iterator(); + + while (iter.hasNext()) { + ((Stmt) iter.next()).cleanup(); + } + + super.clear(); + } + + /** + * Remove a statement from the list and clean up afterwards. + */ + public boolean remove(Object o) { + if (super.remove(o)) { + ((Stmt) o).cleanup(); + return true; + } + + return false; + } + + /** + * Remove all of the statements in a Collection from this statement + * list. + * + * @param c + * A Collection containing the statements to remove. + * @return True, if the contents of this statement list changed. + */ + public boolean removeAll(Collection c) { + boolean changed = false; + + if (c == this) { + changed = size() > 0; + clear(); + } else { + Iterator iter = c.iterator(); + + while (iter.hasNext()) { + changed = remove(iter.next()) || changed; + } + } + + return changed; + } + + /** + * Remove all statements in this list except those that are in a + * specified Collection. + * + * @param c + * The statements to keep. + * @return True, if the contents of this statement list changed. + */ + public boolean retainAll(Collection c) { + boolean changed = false; + + if (c == this) { + return false; + } + + Iterator iter = iterator(); + + while (iter.hasNext()) { + if (!c.contains(iter.next())) { + changed = true; + iter.remove(); + } + } + + return changed; + } + + /** + * Set the value of a given statement. + * + * @param index + * Index of the statement to change. + * @param element + * New value of statement at index. + * + * @return Statement previously at position index. + */ + public Object set(int index, Object element) { + if (index < size()) { + Stmt s = (Stmt) get(index); + + if (s != element) { + s.cleanup(); + } + } + + return super.set(index, element); + } + + /** + * Removes the statement at index + * + * @return Statement previously at position index. + */ + public Object remove(int index) { + Object o = super.remove(index); + + if (o != null) { + ((Stmt) o).cleanup(); + } + + return o; + } + + /** + * Removes statements in a given index range. + */ + /* + * public void removeRange(int fromIndex, int toIndex) { int remaining = + * toIndex - fromIndex; + * + * ListIterator iter = listIterator(fromIndex); + * + * while (iter.hasNext() && remaining-- > 0) { ((Stmt) + * iter.next()).cleanup(); } + * + * super.removeRange(fromIndex, toIndex); } + */ + + /** + * @return A ListIterator starting with the first statement in the + * statement list. + */ + public ListIterator listIterator() { + return listIterator(0); + } + + /** + * @return A ListIterator starting a given index. + */ + public ListIterator listIterator(int index) { + final ListIterator iter = super.listIterator(index); + + return new ListIterator() { + Object last = null; + + public boolean hasNext() { + return iter.hasNext(); + } + + public Object next() { + last = iter.next(); + return last; + } + + public boolean hasPrevious() { + return iter.hasPrevious(); + } + + public Object previous() { + last = iter.previous(); + return last; + } + + public int nextIndex() { + return iter.nextIndex(); + } + + public int previousIndex() { + return iter.previousIndex(); + } + + public void add(Object obj) { + Assert.isTrue(obj instanceof Stmt); + ((Stmt) obj).setParent(Tree.this); + last = null; + iter.add(obj); + } + + public void set(Object obj) { + if (last == null) { + throw new NoSuchElementException(); + } + + Assert.isTrue(obj instanceof Stmt); + ((Stmt) obj).setParent(Tree.this); + + ((Stmt) last).cleanup(); + last = null; + + iter.set(obj); + } + + public void remove() { + if (last == null) { + throw new NoSuchElementException(); + } + + ((Stmt) last).cleanup(); + last = null; + + iter.remove(); + } + }; + } + + /** + * @return An Iterator over this statement list. + */ + public Iterator iterator() { + final Iterator iter = super.iterator(); + + return new Iterator() { + Object last = null; + + public boolean hasNext() { + return iter.hasNext(); + } + + public Object next() { + last = iter.next(); + return last; + } + + public void remove() { + if (last == null) { + throw new NoSuchElementException(); + } + + ((Stmt) last).cleanup(); + last = null; + + iter.remove(); + } + }; + } + } + + /** + * Returns the last non-Label statement in the statement list. + */ + public Stmt lastStmt() { + ListIterator iter = stmts.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + Stmt s = (Stmt) iter.previous(); + if (Tree.DEBUG) + System.out.println("Stmt: " + s); + + if (s instanceof LabelStmt) { + continue; + } + + return s; + } + return null; + } + + /** + * Returns the operand stack. + */ + public OperandStack stack() { + return stack; + } + + /** + * Inserts a statement into the statement list after another given + * statement. + * + * @param stmt + * The statement to add. + * @param after + * The statement after which to add stmt. + */ + public void addStmtAfter(Stmt stmt, Stmt after) { + if (Tree.DEBUG) { + System.out.println("insert: " + stmt + " after " + after); + } + + ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + + if (s == after) { + iter.add(stmt); + stmt.setParent(this); + return; + } + } + + throw new RuntimeException(after + " not found"); + } + + /** + * Inserts a statement into the statement list before a specified statement. + * + * @param stmt + * The statement to insert + * @param before + * The statement before which to add stmt. + */ + public void addStmtBefore(Stmt stmt, Stmt before) { + if (Tree.DEBUG) { + System.out.println("insert: " + stmt + " before " + before); + } + + ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + + if (s == before) { + iter.previous(); + iter.add(stmt); + stmt.setParent(this); + return; + } + } + + throw new RuntimeException(before + " not found"); + } + + /** + * Add an statement to the statement list before the first non-Label + * statement. + * + * @param stmt + * The statement to add. + */ + public void prependStmt(Stmt stmt) { + if (Tree.DEBUG) { + System.out.println("prepend: " + stmt + " in " + block); + } + + ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + + if (!(s instanceof LabelStmt)) { + iter.previous(); + iter.add(stmt); + stmt.setParent(this); + return; + } + } + + appendStmt(stmt); + } + + /** + * When we build the expression tree, there may be items left over on the + * operand stack. We want to save these items. If USE_STACK is true, then we + * place these items into stack variables. If USE_STACK is false, then we + * place the items into local variables. + */ + private void saveStack() { + int height = 0; + + for (int i = 0; i < stack.size(); i++) { + Expr expr = (Expr) stack.get(i); + + if (USE_STACK) { + // Save to a stack variable only if we'll create a new + // variable there. + if (!(expr instanceof StackExpr) + || ((StackExpr) expr).index() != height) { + + StackExpr target = new StackExpr(height, expr.type()); + + // Make a new statement to store the expression that was + // part of the stack into memory. + Stmt store = new ExprStmt(new StoreExpr(target, expr, expr + .type())); + + appendStmt(store); + + StackExpr copy = (StackExpr) target.clone(); + copy.setDef(null); + stack.set(i, copy); + } + + } else { + if (!(expr instanceof LocalExpr) + || !((LocalExpr) expr).fromStack() + || ((LocalExpr) expr).index() != height) { + + LocalExpr target = newStackLocal(nextIndex++, expr.type()); + + Stmt store = new ExprStmt(new StoreExpr(target, expr, expr + .type())); + + appendStmt(store); + + LocalExpr copy = (LocalExpr) target.clone(); + copy.setDef(null); + stack.set(i, copy); + } + } + height += expr.type().getSize(); + } + } + + /** + * Add a statement to this Tree statement list and specify that this is the + * statement's parent. + */ + private void appendStmt(Stmt stmt) { + if (Tree.DEBUG) { + System.out.println(" append: " + stmt); + } + + stmt.setParent(this); + stmts.add(stmt); + } + + /** + * Save the contents of the stack and add stmt to the statement list. + * + * @param stmt + * A statement to add to the statement list. + */ + public void addStmt(Stmt stmt) { + saveStack(); + appendStmt(stmt); + } + + /** + * Adds a statement to the statement list before the last jump statement. It + * is assumed that the last statement in the statement list is a jump + * statement. + * + * @see JumpStmt + */ + public void addStmtBeforeJump(Stmt stmt) { + Stmt last = lastStmt(); + Assert.isTrue(last instanceof JumpStmt, "Last statement of " + block + + " is " + last + ", not a jump"); + addStmtBefore(stmt, last); + } + + /** + * Throw a new ClassFormatException with information about method and class + * that this basic block is in. + * + * @param msg + * String description of the exception. + * + * @see ClassFormatException + */ + private void throwClassFormatException(String msg) { + MethodGen method = block.graph().method(); + + throw new ClassFormatException("Method " + method.getClassName() + "." + + method.getName() + " " + method.getReturnType() + ": " + msg); + } + + /** + * The last instruction we saw. addInst(Instruction) needs this information. + */ + Instruction last = null; + + /** + * Adds an instruction that jumps to another basic block. + * + * @param inst + * The instruction to add. + * @param next + * The basic block after the jump. Remember that a jump ends a + * basic block. + * + * @see Instruction#isJsr + * @see Instruction#isConditionalJump + */ + // Maybe modify to return the InstructionHandle of the added + // Instruction? + public void addInstruction(Instruction inst, Block next) { + Assert.isTrue(inst instanceof JsrInstruction + || inst instanceof IfInstruction, + "Wrong addInstruction called with " + inst); + Assert.isTrue(next != null, "Null next block for " + inst); + + this.next = next; + addInst(inst); + } + + /** + * Adds an instruction that does not change the control flow (a normal + * instruction). + * + * @param inst + * Instruction to add. + */ + // Maybe modify to return the InstructionHandle of the added + // Instruction? + public void addInstruction(Instruction inst) { + Assert.isTrue(!(inst instanceof JsrInstruction) + && !(inst instanceof IfInstruction), + "Wrong addInstruction called with " + inst); + + this.next = null; + addInst(inst); + } + + /** + * Add an instruction such as ret or astore that may involve + * a subroutine. + * + * @param inst + * Instruction to add. + * @param sub + * Subroutine in which inst resides. The ret instruction + * always resides in a Subroutine. An astore may store + * the return address of a subroutine in a local variable. + * + * @see Instruction#isRet + */ + // Maybe modify to return the InstructionHandle of the added + // Instruction? + public void addInstruction(Instruction inst, Subroutine sub) { + Assert.isTrue((inst instanceof RET || inst instanceof ASTORE), + "Wrong addInstruction called with " + inst); + + this.sub = sub; + this.next = null; + addInst(inst); + } + + /** + * Add a label to the statement list. A label is inserted before a dup + * statement, but after any other statement. + * + * @param label + * Label to add. + */ + // HUH? WHY ARE WE ADDING A LABEL + public void addLabel(InstructionHandle label) { + // Add before a dup, but after any other instruction. + if (last != null) { + if ((last instanceof DUP) || (last instanceof DUP2) + || (last instanceof DUP_X1) || (last instanceof DUP2_X1) + || (last instanceof DUP_X2) || (last instanceof DUP2_X2)) { + } else { + addInst(last, false); + last = null; + } + } + + addStmt(new LabelStmt(label)); + } + + /** + * Private method that adds an instruction to the Tree. The visit method of + * the instruction is called with this tree as the visitor. The instruction, + * in turn, calls the appropriate method of this for adding the instruction + * to the statement list. + * + * @param inst + * The Instruction to add to the Tree + * @param saveValue + * Do we save expressions on the operand stack to stack variables + * or local variables. + */ + // Maybe modify to return the InstructionHandle of the added + // Instruction? + private void addInst(Instruction inst, boolean saveValue) { + + if (Tree.DEBUG) { + // Print the contents of the stack + int i = 0; + while (i < stack.size()) { + Expr exp = stack.peek(i); + i = i + exp.type().getSize(); + System.out.println((i > 0 ? "-" + i : " " + i) + ": " + exp); + } + } + + if (Tree.DEBUG) { + System.out.println(" add " + inst + " save=" + saveValue); + } + try { + this.saveValue = saveValue; + + if (FLATTEN) { + saveStack(); + } + visitInst(inst); + + } catch (EmptyStackException e) { + throwClassFormatException("Empty operand stack at " + inst); + return; + } + } + + /** + * Private method that adds an instruction to the tree. Before dispatching + * the addition off to addInst(Instruction, boolean), it optimizes(?) dup + * instructions as outlined below: + */ + // (S0, S1) := dup(X) + // L := S1 --> use (L := X) + // use S0 + // + // (S0, S1, S2) := dup_x1(X, Y) + // S1.f := S2 --> use (X.f := Y) + // use S0 + // + // (S0, S1, S2, S3) := dup_x2(X, Y, Z) + // S1[S2] := S3 --> use (X[Y] := Z) + // use S0 + // + private void addInst(Instruction inst) { + // Build the tree, trying to convert dup-stores + if (last == null) { + last = inst; + + } else { + if (last instanceof DUP) { + if ((inst instanceof ASTORE) || (inst instanceof FSTORE) + || (inst instanceof ISTORE) + || (inst instanceof PUTSTATIC)) { + addInst(inst, true); + last = null; + } + } else if (last instanceof DUP2) { + if ((inst instanceof DSTORE) || (inst instanceof LSTORE) + || (inst instanceof PUTSTATIC)) { + addInst(inst, true); + last = null; + } + } else if (last instanceof DUP_X1) { + if (inst instanceof PUTFIELD) {// PROBLEM HEREcase + // opcx_putfield_nowb: + addInst(inst, true); + last = null; + } + } else if (last instanceof DUP2_X1) { + if (inst instanceof PUTFIELD) {// PROBLEM HEREcase + // opcx_putfield_nowb: + addInst(inst, true); + last = null; + } + } else if (last instanceof DUP_X2) { + if ((inst instanceof AASTORE) || (inst instanceof BASTORE) + || (inst instanceof CASTORE) + || (inst instanceof FASTORE) + || (inst instanceof IASTORE) + || (inst instanceof SASTORE)) { + addInst(inst, true); + last = null; + } + } else if (last instanceof DUP2_X2) { + if ((inst instanceof DASTORE) || (inst instanceof LASTORE)) { + addInst(inst, true); + last = null; + } + } + + if (last != null) { + addInst(last, false); + last = inst; + + } + } + + // We should have dealt with the old last instruction + Assert.isTrue(last == null || last == inst); + + if (inst instanceof BranchInstruction + || inst instanceof ReturnInstruction || inst instanceof RET + || inst instanceof JsrInstruction || inst instanceof Select + || inst instanceof ATHROW) { + addInst(inst, false); + last = null; + } + } + + /** + * Returns a new StackExpr for the top of the operand stack. + */ + public StackExpr newStack(Type type) { + return new StackExpr(stackpos++, type); + } + + /** + * Returns a new LocalExpr that represents an element of the stack. They are + * created when the USE_STACK flag is not set. + * + * @param index + * Stack index of variable. + * @param type + * The type of the LocalExpr + */ + public LocalExpr newStackLocal(int index, Type type) { + if (index >= nextIndex) { + nextIndex = index + 1; + } + return new LocalExpr(index, true, type); + } + + /** + * Returns a new LocalExpr that is not allocated on the stack. + * + * @param index + * Stack index of variable. + * @param type + * The type of the LocalExpr + */ + public LocalExpr newLocal(int index, Type type) { + return new LocalExpr(index, false, type); + } + + /** + * Returns a local variable (LocalExpr) located in this method. + * + * @param type + * The type of the new LocalExpr. + */ + public LocalExpr newLocal(Type type) { + LocalVariableGen var = block.graph().method().addLocalVariable("", + type, null, null); + var.setName("newLocalVar" + var.getIndex()); + // presuming it is bad not to have a name.... + return new LocalExpr(var.getIndex(), type); + } + + /** + * Returns a String representation of this Tree. + */ + public String toString() { + String x = "(TREE " + block + " stack="; + + for (int i = 0; i < stack.size(); i++) { + Expr expr = (Expr) stack.get(i); + x += expr.type(); + } + + return x + ")"; + } + + public void visitInst(Instruction inst) { + if (Tree.DEBUG) + System.out.println("Opcode = " + inst.getOpcode()); + + inst.accept(this); + } + + /** + * Adds no statements to the statement list. + */ + public void visitNOP(NOP inst) { + } + + public void visitDCONST(DCONST inst) { + Expr top = new ConstantExpr((Double) inst.getValue(), Type.DOUBLE); + stack.push(top); + } + + public void visitFCONST(FCONST inst) { + Expr top = new ConstantExpr((Float) inst.getValue(), Type.FLOAT); + stack.push(top); + } + + public void visitICONST(ICONST inst) { + Expr top = new ConstantExpr((Integer) inst.getValue(), Type.INT); + stack.push(top); + } + + public void visitACONST_NULL(ACONST_NULL inst) { + Expr top = new ConstantExpr(null, Type.OBJECT); + stack.push(top); + } + + public void visitLCONST(LCONST inst) { + Expr top = new ConstantExpr((Long) inst.getValue(), Type.LONG); + stack.push(top); + } + + public void visitBIPUSH(BIPUSH inst) { + Expr top = new ConstantExpr(inst.getValue(), Type.BYTE); + stack.push(top); + } + + public void visitSIPUSH(SIPUSH inst) { + Expr top = new ConstantExpr(inst.getValue(), Type.SHORT); + stack.push(top); + } + + /** + * Pushes a ConstantExpr onto the operand stack. + * + * @see ConstantExpr + */ + public void visitLDC2_W(LDC2_W inst) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Object value = inst.getValue(cp); + Type type; + + if (value instanceof Long) { + type = Type.LONG; + } else if (value instanceof Double) { + type = Type.DOUBLE; + } else { + throw new ClassFormatException("Illegal constant type: " + + value.getClass().getName() + ": " + value); + // return; + } + + Expr top = new ConstantExpr(value, type); + stack.push(top); + } + + /** + * Pushes a ConstantExpr onto the operand stack. + * + * @see ConstantExpr + */ + public void visitLDC(LDC inst) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Object value = inst.getValue(cp); + Type type; + + if (value == null) { + type = Type.OBJECT; + } else if (value instanceof Integer) { + type = Type.INT; + } + // else if (value instanceof Long) { + // type = Type.LONG; + // } + else if (value instanceof Float) { + type = Type.FLOAT; + } + // else if (value instanceof Double) { + // type = Type.DOUBLE; + // } + else if (value instanceof String) { + type = Type.STRING; + } else { + throw new ClassFormatException("Illegal constant type: " + + value.getClass().getName() + ": " + value); + // return; + } + + Expr top = new ConstantExpr(value, type); + stack.push(top); + } + + /** + * All visit_xload push a LocalExpr onto the operand + * stack. + * + * @see LocalExpr + */ + + public void visitILOAD(ILOAD iInst) { + Expr top = new LocalExpr(iInst.getIndex(), Type.INT); + stack.push(top); + db(" iload: " + top); + } + + public void visitLLOAD(LLOAD iInst) { + Expr top = new LocalExpr(iInst.getIndex(), Type.LONG); + stack.push(top); + db(" lload: " + top); + } + + public void visitFLOAD(FLOAD iInst) { + Expr top = new LocalExpr(iInst.getIndex(), Type.FLOAT); + stack.push(top); + db(" fload: " + top); + } + + public void visitDLOAD(DLOAD iInst) { + Expr top = new LocalExpr(iInst.getIndex(), Type.DOUBLE); + stack.push(top); + db(" dload: " + top); + } + + public void visitALOAD(ALOAD iInst) { + Expr top = new LocalExpr(iInst.getIndex(), Type.OBJECT); + stack.push(top); + db(" aload: " + top); + } + + /** + * All visit_xaload push an ArrayRefExpr onto the + * operand stack. + */ + public void visitIALOAD(IALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.INT, 1)); + // 0 should do for array dimensions I don't think it is important + Expr top = new ArrayRefExpr(array, index, Type.INT, Type.INT); + stack.push(top); + } + + public void visitLALOAD(LALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.LONG, 1)); + Expr top = new ArrayRefExpr(array, index, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visitFALOAD(FALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.FLOAT, 1)); + Expr top = new ArrayRefExpr(array, index, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visitDALOAD(DALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.DOUBLE, 1)); + Expr top = new ArrayRefExpr(array, index, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visitAALOAD(AALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.OBJECT, 1)); + Expr top = new ArrayRefExpr(array, index, Type.OBJECT, Type.OBJECT); + stack.push(top); + } + + public void visitBALOAD(BALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.BYTE, 1)); + Expr top = new ArrayRefExpr(array, index, Type.BYTE, Type.BYTE); + stack.push(top); + } + + public void visitCALOAD(CALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.CHAR, 1)); + Expr top = new ArrayRefExpr(array, index, Type.CHAR, Type.CHAR); + stack.push(top); + } + + public void visitSALOAD(SALOAD inst) { + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.SHORT, 1)); + Expr top = new ArrayRefExpr(array, index, Type.SHORT, Type.SHORT); + stack.push(top); + } + + /** + * Deals with an expression that stores a value. It either pushes it on the + * operand stack or adds a statement depending on the value of saveValue. + * + * @param target + * The location to where we are storing the value. + * @param expr + * The expression whose value we are storing. + */ + private void addStore(MemExpr target, Expr expr) { + // Stmt last = lastStmt(); + + if (saveValue) { + stack.push(new StoreExpr(target, expr, expr.type())); + + } else { + addStmt(new ExprStmt(new StoreExpr(target, expr, expr.type()))); + } + } + + /** + * All visit_xstore add a LocalExpr statement to the + * statement list. + */ + public void visitISTORE(ISTORE iInst) { + Expr expr = (Expr) stack.pop(Type.INT); + LocalExpr target = new LocalExpr(iInst.getIndex(), expr.type()); + addStore(target, expr); + } + + public void visitLSTORE(LSTORE iInst) { + Expr expr = (Expr) stack.pop(Type.LONG); + LocalExpr target = new LocalExpr(iInst.getIndex(), expr.type()); + addStore(target, expr); + } + + public void visitFSTORE(FSTORE iInst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + LocalExpr target = new LocalExpr(iInst.getIndex(), expr.type()); + addStore(target, expr); + } + + public void visitDSTORE(DSTORE iInst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + LocalExpr target = new LocalExpr(iInst.getIndex(), expr.type()); + addStore(target, expr); + } + + /** + * Visit an astore instruction. If the type of the operand to the + * instruction is an address add an AddressStoreStmt to the tree, else add a + * StoreStmt to the tree consisting of a LocalExpr and the top Expr on the + * operand stack. + * + * @see AddressStoreStmt + * @see LocalExpr + * @see StoreExpr + */ + public void visitASTORE(ASTORE iInst) { + + Expr expr = (Expr) stack.peek(); + + if (expr.type() instanceof ReturnaddressType) { + ReturnaddressType rat = (ReturnaddressType) expr.type(); + Assert.isTrue(sub != null); + Assert.isTrue(!saveValue); + expr = (Expr) stack.pop(ReturnaddressType.NO_TARGET); + // something refering to returnaddressTypes???Can't think of + // anything better + sub.setReturnAddress(rat); + addStmt(new AddressStoreStmt(sub)); + } else { + expr = (Expr) stack.pop(Type.OBJECT); + LocalExpr target = new LocalExpr(iInst.getIndex(), expr.type()); + addStore(target, expr); + } + } + + public void visitIASTORE(IASTORE inst) { + Expr value = (Expr) stack.pop(Type.INT); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.INT, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.INT, Type.INT); + addStore(target, value); + } + + public void visitLASTORE(LASTORE inst) { + Expr value = (Expr) stack.pop(Type.LONG); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.LONG, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.LONG, + Type.LONG); + addStore(target, value); + } + + public void visitFASTORE(FASTORE inst) { + Expr value = (Expr) stack.pop(Type.FLOAT); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.FLOAT, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.FLOAT, + Type.FLOAT); + addStore(target, value); + } + + public void visitDASTORE(DASTORE inst) { + Expr value = (Expr) stack.pop(Type.DOUBLE); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.DOUBLE, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.DOUBLE, + Type.DOUBLE); + addStore(target, value); + } + + public void visitAASTORE(AASTORE inst) { + Expr value = (Expr) stack.pop(Type.OBJECT); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.OBJECT, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.OBJECT, + Type.OBJECT); + addStore(target, value); + } + + public void visitBASTORE(BASTORE inst) { + Expr value = (Expr) stack.pop(Type.BYTE); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.BYTE, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.BYTE, + Type.BYTE); + addStore(target, value); + } + + public void visitCASTORE(CASTORE inst) { + Expr value = (Expr) stack.pop(Type.CHAR); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.CHAR, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.CHAR, + Type.CHAR); + addStore(target, value); + } + + public void visitSASTORE(SASTORE inst) { + Expr value = (Expr) stack.pop(Type.SHORT); + Expr index = (Expr) stack.pop(Type.INT); + Expr array = (Expr) stack.pop(new ArrayType(Type.SHORT, 1)); + ArrayRefExpr target = new ArrayRefExpr(array, index, Type.SHORT, + Type.SHORT); + addStore(target, value); + } + + /** + * Pop the expression off the top of the stack and add it as an ExprStmt to + * the statement list. + * + * @see ExprStmt + */ + public void visitPOP(POP inst) { + Expr expr = stack.pop1(); + addStmt(new ExprStmt(expr)); + } + + public void visitPOP2(POP2 inst) { + Expr[] expr = stack.pop2(); + + if (expr.length == 1) { + addStmt(new ExprStmt(expr[0])); + } else { + addStmt(new ExprStmt(expr[0])); + addStmt(new ExprStmt(expr[1])); + } + } + + /** + * When processing the dup instructions one of two situations can + * occur. If the USE_STACK flag is set, then a StackManipStmt is + * created to represent the transformation that the dup instruction + * performs on the stack. If the USE_STACK flag is not set, then + * the transformation is simulated by creating new local variables + * containing the appropriate element of the stack. + * + * @see LocalExpr + * @see StackExpr + * @see StackManipStmt + */ + public void visitDUP(DUP inst) { + // 0 -> 0 0 + + db(" dup"); + + if (USE_STACK) { + saveStack(); + + StackExpr s0 = (StackExpr) stack.pop1(); + + StackExpr[] s = new StackExpr[] { s0 }; + manip(s, new int[] { 0, 0 }, StackManipStmt.DUP); + + } else { + Expr s0 = stack.pop1(); + + LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + + db(" s0: " + s0); + db(" t0: " + t0); + + if (!t0.equalsExpr(s0)) { + db(" t0 <- s0"); + addStore(t0, s0); + } + + Expr copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + } + } + + public void visitDUP_X1(DUP_X1 inst) { + // 0 1 -> 1 0 1 + + if (USE_STACK) { + saveStack(); + + StackExpr s1 = (StackExpr) stack.pop1(); + StackExpr s0 = (StackExpr) stack.pop1(); + + StackExpr[] s = new StackExpr[] { s0, s1 }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP_X1); + + } else { + Expr s1 = stack.pop1(); + Expr s0 = stack.pop1(); + + LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s1.type()); + + if (!t0.equalsExpr(s0)) { + addStore(t0, s0); + } + + if (!t1.equalsExpr(s1)) { + addStore(t1, s1); + } + + Expr copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + } + } + + public void visitDUP_X2(DUP_X2 inst) { + // 0 1 2 -> 2 0 1 2 + // 0-1 2 -> 2 0-1 2 + + db(" dup_x2"); + + if (USE_STACK) { + saveStack(); + + StackExpr s2 = (StackExpr) stack.pop1(); + Expr[] s01 = stack.pop2(); + + if (s01.length == 2) { + // 0 1 2 -> 2 0 1 2 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1], (StackExpr) s2 }; + manip(s, new int[] { 2, 0, 1, 2 }, StackManipStmt.DUP_X2); + } else { + // 0-1 2 -> 2 0-1 2 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s2 }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP_X2); + } + + } else { + Expr s2 = stack.pop1(); + Expr[] s01 = stack.pop2(); + + db(" s2: " + s2); + db(" s01: " + s01[0] + (s01.length > 1 ? " " + s01[1] : "")); + + if (s01.length == 2) { + // 0 1 2 -> 2 0 1 2 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s2.type()); + + db(" t0: " + t0); + db(" t1: " + t1); + db(" t2: " + t2); + + if (!t0.equalsExpr(s01[0])) { + db(" t0 <- s01[0]"); + addStore(t0, s01[0]); + } + + if (!t1.equalsExpr(s01[1])) { + db(" t1 <- s01[1]"); + addStore(t1, s01[1]); + } + + if (!t2.equalsExpr(s2)) { + db(" t2 <- s2"); + addStore(t2, s2); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + } else { + // 0-1 2 -> 2 0-1 2 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s2.type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t2.equalsExpr(s2)) { + addStore(t2, s2); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + } + } + } + + public void visitDUP2(DUP2 inst) { + // 0 1 -> 0 1 0 1 + // 0-1 -> 0-1 0-1 + + if (USE_STACK) { + saveStack(); + + Expr[] s01 = stack.pop2(); + + if (s01.length == 1) { + // 0-1 -> 0-1 0-1 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0] }; + manip(s, new int[] { 0, 0 }, StackManipStmt.DUP2); + } else { + // 0 1 -> 0 1 0 1 + + Assert.isTrue(s01.length == 2); + + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1] }; + manip(s, new int[] { 0, 1, 0, 1 }, StackManipStmt.DUP2); + } + } else { + Expr[] s01 = stack.pop2(); + + if (s01.length == 1) { + // 0-1 -> 0-1 0-1 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + Expr copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + } else { + // 0 1 -> 0 1 0 1 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t1.equalsExpr(s01[1])) { + addStore(t1, s01[1]); + } + + Expr copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + } + } + } + + public void visitDUP2_X1(DUP2_X1 inst) { + // 0 1 2 -> 1 2 0 1 2 + // 0 1-2 -> 1-2 0 1-2 + + if (USE_STACK) { + saveStack(); + + Expr[] s12 = stack.pop2(); + StackExpr s0 = (StackExpr) stack.pop1(); + + if (s12.length == 2) { + // 0 1 2 -> 1 2 0 1 2 + StackExpr[] s = new StackExpr[] { (StackExpr) s0, + (StackExpr) s12[0], (StackExpr) s12[1] }; + manip(s, new int[] { 1, 2, 0, 1, 2 }, StackManipStmt.DUP2_X1); + } else { + // 0 1-2 -> 1-2 0 1-2 + StackExpr[] s = new StackExpr[] { (StackExpr) s0, + (StackExpr) s12[0] }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP2_X1); + } + } else { + Expr[] s12 = stack.pop2(); + StackExpr s0 = (StackExpr) stack.pop1(); + + if (s12.length == 2) { + // 0 1 2 -> 1 2 0 1 2 + LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s12[0].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s12[1].type()); + + if (!t0.equalsExpr(s0)) { + addStore(t0, s0); + } + + if (!t1.equalsExpr(s12[0])) { + addStore(t1, s12[0]); + } + + if (!t2.equalsExpr(s12[1])) { + addStore(t2, s12[1]); + } + + Expr copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + } else { + // 0 1-2 -> 1-2 0 1-2 + LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s12[0].type()); + + if (!t0.equalsExpr(s0)) { + addStore(t0, s0); + } + + if (!t1.equalsExpr(s12[0])) { + addStore(t1, s12[0]); + } + + Expr copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + } + } + } + + public void visitDUP2_X2(DUP2_X2 inst) { + // 0 1 2 3 -> 2 3 0 1 2 3 + // 0 1 2-3 -> 2-3 0 1 2-3 + // 0-1 2 3 -> 2 3 0-1 2 3 + // 0-1 2-3 -> 2-3 0-1 2-3 + + if (USE_STACK) { + saveStack(); + + Expr[] s23 = stack.pop2(); + Expr[] s01 = stack.pop2(); + + if (s01.length == 2 && s23.length == 2) { + // 0 1 2 3 -> 2 3 0 1 2 3 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1], (StackExpr) s23[0], + (StackExpr) s23[1] }; + manip(s, new int[] { 2, 3, 0, 1, 2, 3 }, StackManipStmt.DUP2_X2); + } else if (s01.length == 2 && s23.length == 1) { + // 0 1 2-3 -> 2-3 0 1 2-3 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1], (StackExpr) s23[0] }; + manip(s, new int[] { 2, 0, 1, 2 }, StackManipStmt.DUP2_X2); + } else if (s01.length == 1 && s23.length == 2) { + // 0-1 2 3 -> 2 3 0-1 2 3 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s23[0], (StackExpr) s23[1] }; + manip(s, new int[] { 1, 2, 0, 1, 2 }, StackManipStmt.DUP2_X2); + } else if (s01.length == 1 && s23.length == 2) { + // 0-1 2-3 -> 2-3 0-1 2-3 + StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s23[0] }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP2_X2); + } + } else { + Expr[] s23 = stack.pop2(); + Expr[] s01 = stack.pop2(); + + if (s01.length == 2 && s23.length == 2) { + // 0 1 2 3 -> 2 3 0 1 2 3 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0].type()); + LocalExpr t3 = newStackLocal(stack.height() + 3, s23[1].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t1.equalsExpr(s01[1])) { + addStore(t1, s01[1]); + } + + if (!t2.equalsExpr(s23[0])) { + addStore(t2, s23[0]); + } + + if (!t3.equalsExpr(s23[1])) { + addStore(t3, s23[1]); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t3.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t3.clone(); + copy.setDef(null); + stack.push(copy); + } else if (s01.length == 2 && s23.length == 1) { + // 0 1 2-3 -> 2-3 0 1 2-3 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t1.equalsExpr(s01[1])) { + addStore(t1, s01[1]); + } + + if (!t2.equalsExpr(s23[0])) { + addStore(t2, s23[0]); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + } else if (s01.length == 1 && s23.length == 2) { + // 0-1 2 3 -> 2 3 0-1 2 3 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0].type()); + LocalExpr t3 = newStackLocal(stack.height() + 3, s23[1].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t2.equalsExpr(s23[0])) { + addStore(t2, s23[0]); + } + + if (!t3.equalsExpr(s23[1])) { + addStore(t3, s23[1]); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t3.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t3.clone(); + copy.setDef(null); + stack.push(copy); + } else if (s01.length == 1 && s23.length == 2) { + // 0-1 2-3 -> 2-3 0-1 2-3 + LocalExpr t0 = newStackLocal(stack.height(), s01[0].type()); + LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0].type()); + + if (!t0.equalsExpr(s01[0])) { + addStore(t0, s01[0]); + } + + if (!t2.equalsExpr(s23[0])) { + addStore(t2, s23[0]); + } + + Expr copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t2.clone(); + copy.setDef(null); + stack.push(copy); + } + } + } + + public void visitSWAP(SWAP inst) { + // 0 1 -> 1 0 + + if (USE_STACK) { + saveStack(); + + StackExpr s1 = (StackExpr) stack.pop1(); + StackExpr s0 = (StackExpr) stack.pop1(); + + StackExpr[] s = new StackExpr[] { (StackExpr) s0, (StackExpr) s1 }; + manip(s, new int[] { 1, 0 }, StackManipStmt.SWAP); + } else { + Expr s1 = stack.pop1(); + Expr s0 = stack.pop1(); + + LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + LocalExpr t1 = newStackLocal(stack.height() + 1, s1.type()); + + if (!t0.equalsExpr(s0)) { + addStore(t0, s0); + } + + if (!t1.equalsExpr(s1)) { + addStore(t1, s1); + } + + Expr copy = (Expr) t1.clone(); + copy.setDef(null); + stack.push(copy); + + copy = (Expr) t0.clone(); + copy.setDef(null); + stack.push(copy); + } + } + + /** + * Produces a StackManipStmt that represents how the stack is changed as a + * result of a dup instruction. It should only be used when USE_STACK is + * true. + *

      + * dup instructions change the top n elements of the JVM stack. This method + * takes the original top n elements of the stack, an integer array + * representing the transformation (for instance, if s[0] = 1, then the top + * element of the new stack should contain the second-from-the-top element + * of the old stack), and integer representing the dup instruction. + * + * @param source + * The interesting part of the stack before the dup instruction + * is executed. + * @param s + * An integer array representing the new order of the stack. + * @param kind + * The kind of stack manipulation taking place. (e.g. + * StackManipStmt.DUP_X1) + * + * @see StackManipStmt + */ + private void manip(StackExpr[] source, int[] s, int kind) { + Assert.isTrue(USE_STACK); + + int height = 0; // Height of the stack + + // Calculate current height of the stack. Recall that the stack + // elements in source have already been popped off the stack. + for (int i = 0; i < stack.size(); i++) { + Expr expr = (Expr) stack.get(i); + height += expr.type().getSize(); + } + + // Create the new portion of the stack. Make new StackExpr + // representing the stack after the dup instruction. Push those + // new StackExprs onto the operand stack. Finally, create a + // StackManipStmt that represent the transformation of the old + // stack (before dup instruction) to the new stack (after dup + // instruction). + StackExpr[] target = new StackExpr[s.length]; + + for (int i = 0; i < s.length; i++) { + target[i] = new StackExpr(height, source[s[i]].type()); + StackExpr copy = (StackExpr) target[i].clone(); + copy.setDef(null); + stack.push(copy); + height += target[i].type().getSize(); + } + + appendStmt(new StackManipStmt(target, source, kind)); + } + + /** + * All visit_xadd, visit_xsub, + * visit_xmul, visit_xdiv, etc. + * push an ArithExpr onto the operand stack. + * + * @see ArithExpr + */ + public void visitIADD(IADD inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.INT); + stack.push(top); + } + + public void visitLADD(LADD inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.LONG); + stack.push(top); + } + + public void visitFADD(FADD inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.FLOAT); + stack.push(top); + } + + public void visitDADD(DADD inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visitISUB(ISUB inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.INT); + stack.push(top); + } + + public void visitLSUB(LSUB inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.LONG); + stack.push(top); + } + + public void visitFSUB(FSUB inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.FLOAT); + stack.push(top); + } + + public void visitDSUB(DSUB inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visitIMUL(IMUL inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.INT); + stack.push(top); + } + + public void visitLMUL(LMUL inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.LONG); + stack.push(top); + } + + public void visitFMUL(FMUL inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.FLOAT); + stack.push(top); + } + + public void visitDMUL(DMUL inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visitIDIV(IDIV inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr check = new ZeroCheckExpr(right, Type.INT); + Expr top = new ArithExpr(ArithExpr.DIV, left, check, Type.INT); + stack.push(top); + } + + public void visitLDIV(LDIV inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr check = new ZeroCheckExpr(right, Type.LONG); + Expr top = new ArithExpr(ArithExpr.DIV, left, check, Type.LONG); + stack.push(top); + } + + public void visitFDIV(FDIV inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.DIV, left, right, Type.FLOAT); + stack.push(top); + } + + public void visitDDIV(DDIV inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.DIV, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visitIREM(IREM inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr check = new ZeroCheckExpr(right, Type.INT); + Expr top = new ArithExpr(ArithExpr.REM, left, check, Type.INT); + stack.push(top); + } + + public void visitLREM(LREM inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr check = new ZeroCheckExpr(right, Type.LONG); + Expr top = new ArithExpr(ArithExpr.REM, left, check, Type.LONG); + stack.push(top); + } + + public void visitFREM(FREM inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.REM, left, right, Type.FLOAT); + stack.push(top); + } + + public void visitDREM(DREM inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.REM, left, right, Type.DOUBLE); + stack.push(top); + } + + /** + * All visit_xneg push a NegExpr onto the stack. + * + * @see NegExpr + */ + public void visitINEG(INEG inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new NegExpr(expr, Type.INT); + stack.push(top); + } + + public void visitLNEG(LNEG inst) { + Expr expr = (Expr) stack.pop(Type.LONG); + Expr top = new NegExpr(expr, Type.LONG); + stack.push(top); + } + + public void visitFNEG(FNEG inst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + Expr top = new NegExpr(expr, Type.FLOAT); + stack.push(top); + } + + public void visitDNEG(DNEG inst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + Expr top = new NegExpr(expr, Type.DOUBLE); + stack.push(top); + } + + /** + * All visit_xshd push a ShiftExpr onto the + * operand stack. + * + * @see ShiftExpr + */ + public void visitISHL(ISHL inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ShiftExpr(ShiftExpr.LEFT, left, right, Type.INT); + stack.push(top); + } + + public void visitLSHL(LSHL inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ShiftExpr(ShiftExpr.LEFT, left, right, Type.LONG); + stack.push(top); + } + + public void visitISHR(ISHR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ShiftExpr(ShiftExpr.RIGHT, left, right, Type.INT); + stack.push(top); + } + + public void visitLSHR(LSHR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ShiftExpr(ShiftExpr.RIGHT, left, right, Type.LONG); + stack.push(top); + } + + public void visitIUSHR(IUSHR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ShiftExpr(ShiftExpr.UNSIGNED_RIGHT, left, right, + Type.INT); + stack.push(top); + } + + public void visitLUSHR(LUSHR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ShiftExpr(ShiftExpr.UNSIGNED_RIGHT, left, right, + Type.LONG); + stack.push(top); + } + + /** + * All visit_x op push an ArithExpr onto the stack. + * + * @see ArithExpr + */ + public void visitIAND(IAND inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.AND, left, right, Type.INT); + stack.push(top); + } + + public void visitLAND(LAND inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.AND, left, right, Type.LONG); + stack.push(top); + } + + public void visitIOR(IOR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.IOR, left, right, Type.INT); + stack.push(top); + } + + public void visitLOR(LOR inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.IOR, left, right, Type.LONG); + stack.push(top); + } + + public void visitIXOR(IXOR inst) { + Expr right = (Expr) stack.pop(Type.INT); + Expr left = (Expr) stack.pop(Type.INT); + Expr top = new ArithExpr(ArithExpr.XOR, left, right, Type.INT); + stack.push(top); + } + + public void visitLXOR(LXOR inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.XOR, left, right, Type.LONG); + stack.push(top); + } + + /** + * Visiting an iinc involves creating a ConstantExpr, LocalExpr, ArithExpr + * StoreExpr, and a ExprStmt. + */ + public void visitIINC(IINC iInc) { + int incr = iInc.getIncrement(); + + if (incr < 0) { + Expr right = new ConstantExpr(new Integer(-incr), Type.INT); + Expr left = new LocalExpr(iInc.getIndex(), Type.INT); + + Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.INT); + + LocalExpr copy = (LocalExpr) left.clone(); + copy.setDef(null); + addStmt(new ExprStmt(new StoreExpr(copy, top, left.type()))); + } else if (incr > 0) { + Expr right = new ConstantExpr(new Integer(incr), Type.INT); + Expr left = new LocalExpr(iInc.getIndex(), Type.INT); + + Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.INT); + + LocalExpr copy = (LocalExpr) left.clone(); + copy.setDef(null); + addStmt(new ExprStmt(new StoreExpr(copy, top, left.type()))); + } + } + + /** + * All cast visitors push a CastExpr onto the operand stack. + */ + public void visitI2L(I2L inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visitI2F(I2F inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visitI2D(I2D inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visitL2I(L2I inst) { + Expr expr = (Expr) stack.pop(Type.LONG); + Expr top = new CastExpr(expr, Type.INT, Type.INT); + stack.push(top); + } + + public void visitL2F(L2F inst) { + Expr expr = (Expr) stack.pop(Type.LONG); + Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visitL2D(L2D inst) { + Expr expr = (Expr) stack.pop(Type.LONG); + Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visitF2I(F2I inst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + Expr top = new CastExpr(expr, Type.INT, Type.INT); + stack.push(top); + } + + public void visitF2L(F2L inst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visitF2D(F2D inst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visitD2I(D2I inst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + Expr top = new CastExpr(expr, Type.INT, Type.INT); + stack.push(top); + } + + public void visitD2L(D2L inst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visitD2F(D2F inst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visitI2B(I2B inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.BYTE, Type.INT); + stack.push(top); + } + + public void visitI2C(I2C inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.CHAR, Type.INT); + stack.push(top); + } + + public void visitI2S(I2S inst) { + Expr expr = (Expr) stack.pop(Type.INT); + Expr top = new CastExpr(expr, Type.SHORT, Type.INT); + stack.push(top); + } + + /** + * All visit_xcmp push an ArithExpr onto the stack. + * + * @see ArithExpr + */ + public void visitLCMP(LCMP inst) { + Expr right = (Expr) stack.pop(Type.LONG); + Expr left = (Expr) stack.pop(Type.LONG); + Expr top = new ArithExpr(ArithExpr.CMP, left, right, Type.INT); + stack.push(top); + } + + public void visitFCMPL(FCMPL inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.CMPL, left, right, Type.INT); + stack.push(top); + } + + public void visitFCMPG(FCMPG inst) { + Expr right = (Expr) stack.pop(Type.FLOAT); + Expr left = (Expr) stack.pop(Type.FLOAT); + Expr top = new ArithExpr(ArithExpr.CMPG, left, right, Type.INT); + stack.push(top); + } + + public void visitDCMPL(DCMPL inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.CMPL, left, right, Type.INT); + stack.push(top); + } + + public void visitDCMPG(DCMPG inst) { + Expr right = (Expr) stack.pop(Type.DOUBLE); + Expr left = (Expr) stack.pop(Type.DOUBLE); + Expr top = new ArithExpr(ArithExpr.CMPG, left, right, Type.INT); + stack.push(top); + } + + /** + * All visit_xeg add an IfZeroStmt to the statement + * list. + * + * @see IfZeroStmt + */ + public void visitIFEQ(IFEQ bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + // this could also maybe used getIndex?? + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.EQ, left, t, next)); + } + + public void visitIFNE(IFNE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + // this could also maybe used getIndex?? + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.NE, left, t, next)); + } + + public void visitIFLT(IFLT bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.LT, left, t, next)); + } + + public void visitIFGE(IFGE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.GE, left, t, next)); + } + + public void visitIFGT(IFGT bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.GT, left, t, next)); + } + + public void visitIFLE(IFLE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfZeroStmt(IfStmt.LE, left, t, next)); + } + + /** + * All visit_if_xcmpy add a IfCmpStmt to the + * statement list. + */ + public void visitIF_ICMPEQ(IF_ICMPEQ bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.EQ, left, right, t, next)); + } + + public void visitIF_ICMPNE(IF_ICMPNE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.NE, left, right, t, next)); + } + + public void visitIF_ICMPLT(IF_ICMPLT bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.LT, left, right, t, next)); + } + + public void visitIF_ICMPGE(IF_ICMPGE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.GE, left, right, t, next)); + } + + public void visitIF_ICMPGT(IF_ICMPGT bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.GT, left, right, t, next)); + } + + public void visitIF_ICMPLE(IF_ICMPLE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.INT); + Expr left = stack.pop(Type.INT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.LE, left, right, t, next)); + } + + public void visitIF_ACMPEQ(IF_ACMPEQ bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.OBJECT); + Expr left = stack.pop(Type.OBJECT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.EQ, left, right, t, next)); + } + + public void visitIF_ACMPNE(IF_ACMPNE bInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr right = stack.pop(Type.OBJECT); + Expr left = stack.pop(Type.OBJECT); + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + + addStmt(new IfCmpStmt(IfStmt.NE, left, right, t, next)); + } + + /** + * Adds a GotoStmt to the statement list. + * + * @see GotoStmt + */ + public void visitGOTO(GOTO bInst) { + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + addStmt(new GotoStmt(t)); + } + + public void visitGOTO_W(GOTO_W bInst) { + Block t = (Block) block.graph().getNode(bInst.getTarget()); + Assert.isTrue(t != null, "No block for " + bInst); + addStmt(new GotoStmt(t)); + } + + /** + * Adds a JsrStmt to the statement list. + * + * @see JsrStmt + */ + public void visitJsrInstruction(JsrInstruction bInst) { + // Push the return address after we add the statement. + // This prevents it from being saved to a local variable. + // It's illegal to load a return address from a local variable, + // so we can't save it. + Subroutine sub = block.graph().labelSub(bInst.getTarget()); + addStmt(new JsrStmt(sub, next)); + stack.push(new ReturnAddressExpr(ReturnaddressType.NO_TARGET)); + // AGAIN THIS MAY NOT BE THE BEST + } + + /** + * Adds a RetStmt to the statement list. + * + * @see RetStmt + */ + public void visitRET(RET inst) { + Assert.isTrue(sub != null); + addStmt(new RetStmt(sub)); + } + + /** + * Add a SwitchStmt to the statement list. + * + * @see SwitchStmt + */ + public void visitSelect(Select sInst) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + Expr index = stack.pop(Type.INT); + + Block defaultTarget = (Block) block.graph().getNode(sInst.getTarget()); + Assert.isTrue(defaultTarget != null, "No block for " + sInst); + + InstructionHandle[] targ = sInst.getTargets(); + Block[] targets = new Block[targ.length]; + + for (int i = 0; i < targets.length; i++) { + targets[i] = (Block) block.graph().getNode(targ[i]); + Assert.isTrue(targets[i] != null, "No block for " + sInst); + } // LABEL STUFF DONE HERE + + addStmt(new SwitchStmt(index, defaultTarget, targets, sInst.getMatchs())); + } + + /** + * All visit_xreturn add a ReturnExprStmt to the + * statement list. + * + * @see ReturnExprStmt + */ + public void visitIRETURN(IRETURN inst) { + Expr expr = (Expr) stack.pop(Type.INT); + addStmt(new ReturnExprStmt(expr)); + } + + public void visitLRETURN(LRETURN inst) { + Expr expr = (Expr) stack.pop(Type.LONG); + addStmt(new ReturnExprStmt(expr)); + } + + public void visitFRETURN(FRETURN inst) { + Expr expr = (Expr) stack.pop(Type.FLOAT); + addStmt(new ReturnExprStmt(expr)); + } + + public void visitDRETURN(DRETURN inst) { + Expr expr = (Expr) stack.pop(Type.DOUBLE); + addStmt(new ReturnExprStmt(expr)); + } + + public void visitARETURN(ARETURN inst) { + Expr expr = (Expr) stack.pop(Type.OBJECT); + addStmt(new ReturnExprStmt(expr)); + } + + /** + * Adds a ReturnStmt to the statement list. + */ + public void visitRETURN(RETURN inst) { + addStmt(new ReturnStmt()); + } + + /** + * Pushes a StaticFieldExpr onto the operand stack. + */ + public void visitGETSTATIC(GETSTATIC statInst) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + MemberRef field = new MemberRef((ObjectType) statInst + .getReferenceType(cp), new NameAndType(statInst + .getFieldName(cp), statInst.getFieldType(cp))); + Type type = statInst.getFieldType(cp); + try { + JavaClass jc = org.apache.bcel.Repository.lookupClass(statInst + .getLoadClassType(cp).getClassName()); + if (Tree.DEBUG && jc == null) + System.out.println("ARGGGH: no class found " + + statInst.getLoadClassType(cp).getClassName()); + Field[] fields = jc.getFields(); + int j = -1; + for (int i = 0; i < fields.length; i++) { + if (fields[i].getName().equals(statInst.getFieldName(cp))) { + j = i; + break; + } + } + if (j == -1) { + throw new NoSuchFieldException(); + } + Field e = fields[j]; + + if (e.isFinal()) { + if (e.getConstantValue() != null) {// WHY??MAYBE DODGY? + // There doesn't seem to be much way to check the + // constant's value in BCEL + // and BLOAT's is an Object so could be anything... + int index = (e.getConstantValue()).getConstantValueIndex(); + Constant c = cp.getConstant(index); + if ((c instanceof ConstantObject) + && (c instanceof ConstantClass)) { + ConstantObject co = (ConstantObject) c; + Expr top = new ConstantExpr(co.getConstantValue(cp + .getConstantPool()), type); + stack.push(top); + // context.release(e.fieldInfo());//We never started + // editing it + return; + } + } + } + + // context.release(e.fieldInfo());//We never started editing it + } catch (NoSuchFieldException e) { + // No field found. Silently assume non-final. + } catch (ClassNotFoundException e) { + // Ignore for the moment. + } + + Expr top = new StaticFieldExpr(field, type); + stack.push(top); + } + + public void visitPUTSTATIC(PUTSTATIC pS) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + MemberRef field = new MemberRef((ObjectType) pS.getReferenceType(cp), + new NameAndType(pS.getFieldName(cp), pS.getFieldType(cp))); + Type type = field.nameAndType().type(); + Expr value = stack.pop(type); + StaticFieldExpr target = new StaticFieldExpr(field, type); + addStore(target, value); + } + + // public void visit_putstatic_nowb( inst) { + // visit_putstatic(inst); + // } + + /** + * Pushes a FieldExpr onto the operand stack. + */ + public void visitGETFIELD(GETFIELD gF) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + MemberRef field = new MemberRef((ObjectType) gF.getReferenceType(cp), + new NameAndType(gF.getFieldName(cp), gF.getFieldType(cp))); + Type type = field.nameAndType().type(); + Expr obj = stack.pop(Type.OBJECT); + Expr check = new ZeroCheckExpr(obj, obj.type()); + Expr top = new FieldExpr(check, field, type); + stack.push(top); + } + + public void visitPUTFIELD(PUTFIELD pF) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + MemberRef field = new MemberRef((ObjectType) pF.getReferenceType(cp), + new NameAndType(pF.getFieldName(cp), pF.getFieldType(cp))); + Type type = field.nameAndType().type(); + Expr value = stack.pop(type); + Expr obj = stack.pop(Type.OBJECT); + Expr ucCheck = obj; + + if (USE_PERSISTENT) + ucCheck = new UCExpr(obj, UCExpr.POINTER, obj.type()); + + Expr check = new ZeroCheckExpr(ucCheck, obj.type()); + + new FieldExpr(check, field, type); + + FieldExpr target = new FieldExpr(check, field, type); + addStore(target, value); + } + + // Don't insert UCExpr + /* + * Persistentcy Stuff public void visit_putfield_nowb(Instruction inst) { + * PUTFIELD pF = (PUTFIELD)inst; ConstantPoolGen cp = + * block.graph().method().getConstantPool(); MemberRef field = new + * MemberRef(pF.getClassType(cp), new NameAndType(pF.getFieldName(cp), + * pF.getFieldType(cp))); Type type = field.nameAndType().type(); Expr value = + * stack.pop(type); Expr obj = stack.pop(Type.OBJECT); Expr check = new + * ZeroCheckExpr(obj, obj.type()); Expr top = new FieldExpr(check, field, + * type); FieldExpr target = new FieldExpr(check, field, type); + * addStore(target, value); } + */ + + /** + * All visit_invokex deal with a CallMethodExpr or a + * CallStaticExpr. + * + * @see CallMethodExpr + * @see CallStaticExpr + */ + public void visitINVOKEVIRTUAL(INVOKEVIRTUAL inst) { + addCall(inst, CallMethodExpr.VIRTUAL); + } + + public void visitINVOKESPECIAL(INVOKESPECIAL inst) { + addCall(inst, CallMethodExpr.NONVIRTUAL); + } + + public void visitINVOKESTATIC(INVOKESTATIC inst) { + addCall(inst, 0); + } + + public void visitINVOKEINTERFACE(INVOKEINTERFACE inst) { + addCall(inst, CallMethodExpr.INTERFACE); + } + + /** + * Creates either a CallMethodExpr or a CallStaticExpr to represent a method + * call. After obtaining some information about the method. The parameters + * to the methods are popped from the operand stack. If the method is not + * static, the "this" object is popped from the operand stack. A + * CallMethodExpr is created for a non-static method and a CallStaticExpr is + * created for a static method. If the method returns a value, the Call*Expr + * is pushed onto the stack. If the method has no return value, it is + * wrapped in an ExprStmt and is added to the statement list. + */ + private void addCall(InvokeInstruction iI, int kind) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Type[] paramTypes = iI.getArgumentTypes(cp); + Type returnType = iI.getReturnType(cp); + ReferenceType caller = iI.getReferenceType(cp); + + MethodRef method = new MethodRef(caller, new NameAndType(iI + .getMethodName(cp), returnType), returnType, paramTypes); + + Expr[] params = new Expr[paramTypes.length]; + + for (int i = paramTypes.length - 1; i >= 0; i--) { + params[i] = stack.pop(paramTypes[i]); + } + + Expr top; + + if (!(iI instanceof INVOKESTATIC)) { + Expr obj = stack.pop(Type.OBJECT); + + top = new CallMethodExpr(kind, obj, params, method); + + } else { + top = new CallStaticExpr(params, method); + } + + if (returnType.equals(Type.VOID)) { + addStmt(new ExprStmt(top)); + + } else { + stack.push(top); + } + } + + /** + * Pushes a NewExpr onto the operand stack. + * + * @see NewExpr + */ + public void visitNEW(NEW nI) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Type type = nI.getLoadClassType(cp); + Expr top = new NewExpr(type, Type.OBJECT); + stack.push(top); + + db(" new: " + top); + } + + /** + * Pushes a NewArrayExpr onto the operand stack. + */ + public void visitNEWARRAY(NEWARRAY nAI) { + BasicType type = (BasicType) ((ArrayType) nAI.getType()).getBasicType(); + Expr size = stack.pop(Type.INT); + Expr top = new NewArrayExpr(size, type, (ArrayType) nAI.getType()); + stack.push(top); + } + + /** + * Pushes a NewArrayExpr onto the operand stack. + */ + public void visitANEWARRAY(ANEWARRAY nAI) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Expr size = stack.pop(Type.INT); + Expr top = new NewArrayExpr(size, nAI.getType(cp), new ArrayType(nAI + .getType(cp), 1)); + stack.push(top); + } + + /** + * Pushes an ArrayLengthExpr onto the operand stack. + * + * @see ArrayLengthExpr + */ + public void visitARRAYLENGTH(ARRAYLENGTH inst) { + Expr array = (Expr) stack.pop(Type.OBJECT); + Expr top = new ArrayLengthExpr(array, Type.INT); + stack.push(top); + } + + /** + * Adds a ThrowStmt to the statement list. + * + * @see ThrowStmt + */ + public void visitATHROW(ATHROW inst) { + Expr expr = (Expr) stack.pop(ObjectType.THROWABLE); + addStmt(new ThrowStmt(expr)); + } + + /** + * Pushes a CastExpr onto the operand stack. + * + * @see CastExpr + */ + public void visitCHECKCAST(CHECKCAST cCI) { + Expr expr = stack.pop(Type.OBJECT); + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Type type = cCI.getType(cp); + Expr top = new CastExpr(expr, type, type); + stack.push(top); + } + + /** + * Pushes an InstanceOfExpr onto the operand stack. + * + * @see InstanceOfExpr + */ + public void visitINSTANCEOF(INSTANCEOF iOI) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + Type type = iOI.getType(cp); + Expr expr = (Expr) stack.pop(Type.OBJECT); + Expr top = new InstanceOfExpr(expr, type, Type.INT); + stack.push(top); + } + + /** + * Both monitor visitors add a MonitorStmt to the statement list. + * + * @see MonitorStmt + */ + public void visitMONITORENTER(MONITORENTER inst) { + Expr obj = (Expr) stack.pop(Type.OBJECT); + addStmt(new MonitorStmt(MonitorStmt.ENTER, obj)); + } + + public void visitMONITOREXIT(MONITOREXIT inst) { + Expr obj = (Expr) stack.pop(Type.OBJECT); + addStmt(new MonitorStmt(MonitorStmt.EXIT, obj)); + } + + /** + * Push a NewMultiArrayExpr onto the operand stack. + * + * @see NewMultiArrayExpr + */ + public void visitMULTIANEWARRAY(MULTIANEWARRAY mANAI) { + ConstantPoolGen cp = block.graph().method().getConstantPool(); + + Expr[] dim = new Expr[mANAI.getDimensions()]; + + for (int i = dim.length - 1; i >= 0; i--) { + dim[i] = stack.pop(Type.INT); + } + + Type type = mANAI.getType(cp); + Expr top = new NewMultiArrayExpr(dim, type, new ArrayType(type, + dim.length)); + + stack.push(top); + } + + /** + * Both visit_xnull add an IfZeroStmt to the statement + * list. + * + * ssee IfZeroStmt + */ + public void visitIFNULL(IFNULL bI) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + Expr left = stack.pop(Type.OBJECT); + + Block t = (Block) block.graph().getNode(bI.getTarget()); + Assert.isTrue(t != null, "No block for " + bI); + + addStmt(new IfZeroStmt(IfStmt.EQ, left, t, next)); + } + + public void visitIFNONNULL(IFNONNULL bI) { + // It isn't necessary to saveStack since removing critical edges + // guarantees that no code will be inserted before the branch + // since there cannot be more than one predecessor of either of + // the successor nodes (and hence no phi statements, even in SSAPRE). + // saveStack(); + + Expr left = stack.pop(Type.OBJECT); + Block t = (Block) block.graph().getNode(bI.getTarget()); + Assert.isTrue(t != null, "No block for " + bI); + + addStmt(new IfZeroStmt(IfStmt.NE, left, t, next)); + } + + /** + * Replaces the expression on the top of the stack with an RCExpr. + * + * @see RCExpr + */ + // Persistency Stuff? + /* + * public void visit_rc(Instruction inst) { Integer depth = (Integer) + * inst.operand(); Expr object = stack.peek(depth.intValue()); + * stack.replace(depth.intValue(), new RCExpr(object, object.type())); } + */ + /** + * + */ + // More Persistency Stuff + /* + * public void visit_aupdate(Instruction inst) { Integer depth = (Integer) + * inst.operand(); + * + * if (AUPDATE_FIX_HACK) { // Hack to fix a bug in old bloat-generated code: + * + * if (depth.intValue() == 1) { Expr object = stack.peek(); + * + * if (object.type().isWide()) { depth = new Integer(2); + * inst.setOperand(depth); AUPDATE_FIX_HACK_CHANGED = true; } } } + * + * Expr object = stack.peek(depth.intValue()); + * stack.replace(depth.intValue(), new UCExpr(object, UCExpr.POINTER, + * object.type())); } + */ + /** + * Replace the expression at the stack depth specified in the instruction + * with a UCExpr. + * + * @see UCExpr + */ + // More Persistency Stuff + /* + * public void visit_supdate(Instruction inst) { Integer depth = (Integer) + * inst.operand(); Expr object = stack.peek(depth.intValue()); + * stack.replace(depth.intValue(), new UCExpr(object, UCExpr.SCALAR, + * object.type())); } + */ + /** + * Add a SCStmt to the statement list + * + * @see SCStmt + */ + // Tempory we might have to Create a ASWIZZLE instruction? + /* + * public void visit_aswizzle(ASWIZZLE inst) { Expr index = (Expr) + * stack.pop(Type.INT); Expr array = (Expr) stack.pop(new + * ArrayType(Type.OBJECT,1)); + * + * addStmt(new SCStmt(array, index)); } + */ + /** + * Add a SRStmt to the statement list. + * + * @see SRStmt + */ + /* + * public void visit_aswrange(Instruction inst) { Expr end = (Expr) + * stack.pop(Type.INT); Expr start = (Expr) stack.pop(Type.INT); Expr array = + * (Expr) stack.pop(new ArrayType(Type.OBJECT,1)); + * + * addStmt(new SRStmt(array, start, end)); } + */ + /** + * Visit all the statements in the statement list. + */ + public void visitForceChildren(TreeVisitor visitor) { + LinkedList list = new LinkedList(stmts); + + if (visitor.reverse()) { + ListIterator iter = list.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + Stmt s = (Stmt) iter.previous(); + s.visit(visitor); + } + } else { + ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + Stmt s = (Stmt) iter.next(); + s.visit(visitor); + } + } + } + + public void visit(TreeVisitor visitor) { + visitor.visitTree(this); + } + + public Node parent() { + return null; + } + + public Block block() { + return block; + } + + public void visitBREAKPOINT(BREAKPOINT inst) { + } + + public void visitLOOKUPSWITCH(LOOKUPSWITCH inst) { + } + + public void visitIMPDEP1(IMPDEP1 inst) { + } + + public void visitIMPDEP2(IMPDEP2 inst) { + } + + public void visitTABLESWITCH(TABLESWITCH inst) { + } + + public void visitStackConsumer(StackConsumer inst) { + } + + public void visitStackProducer(StackProducer inst) { + } + + public void visitVariableLengthInstruction(VariableLengthInstruction inst) { + } + + public void visitLoadInstruction(LoadInstruction inst) { + } + + public void visitExceptionThrower(ExceptionThrower inst) { + } + + public void visitConstantPushInstruction(ConstantPushInstruction inst) { + } + + public void visitFieldOrMethod(org.apache.bcel.generic.FieldOrMethod inst) { + } + + public void visitReturnInstruction(ReturnInstruction inst) { + } + + public void visitAllocationInstruction(AllocationInstruction inst) { + } + + public void visitArrayInstruction(ArrayInstruction inst) { + } + + public void visitInvokeInstruction(InvokeInstruction inst) { + } + + public void visitCPInstruction(CPInstruction inst) { + } + + public void visitArithmeticInstruction(ArithmeticInstruction inst) { + } + + public void visitPushInstruction(PushInstruction inst) { + } + + public void visitUnconditionalBranch(UnconditionalBranch inst) { + } + + public void visitGotoInstruction(GotoInstruction inst) { + } + + public void visitTypedInstruction(TypedInstruction inst) { + } + + public void visitStoreInstruction(StoreInstruction inst) { + } + + public void visitPopInstruction(PopInstruction inst) { + } + + public void visitConversionInstruction(ConversionInstruction inst) { + } + + public void visitIfInstruction(IfInstruction inst) { + } + + public void visitFieldInstruction(FieldInstruction inst) { + } + + public void visitLoadClass(LoadClass inst) { + } + + public void visitBranchInstruction(BranchInstruction inst) { + } + + public void visitLocalVariableInstruction(LocalVariableInstruction inst) { + } + + public void visitStackInstruction(StackInstruction inst) { + } + + public void visitJSR(JSR inst) { + } + + public void visitJSR_W(JSR_W inst) { + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/TreeVisitor.java b/src/edu/purdue/cs/bloat/tree/TreeVisitor.java new file mode 100644 index 0000000..7d0f908 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/TreeVisitor.java @@ -0,0 +1,329 @@ +/* + * Class: TreeVisitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import edu.purdue.cs.bloat.cfg.Block; +import edu.purdue.cs.bloat.cfg.FlowGraph; + +/** + * TreeVisitor performs a traversal of a tree. It does so by having a method of + * every kind of node in the tree. This abstract class performs default + * operations for each kind of node visited. It must be subclasses to perform a + * more interesting traversal. + * + * @see Node + * @see Tree + * + * @see PrintVisitor + * @see ReplaceVisitor + * + */ +public abstract class TreeVisitor { + public static final int FORWARD = 0; + + public static final int REVERSE = 1; + + boolean prune; + + int direction; + + public TreeVisitor() { + this(FORWARD); + } + + public TreeVisitor(int direction) { + this.direction = direction; + } + + /** + * @param prune + * Is the tree pruned during traversal? + */ + public void setPrune(boolean prune) { + this.prune = prune; + } + + public boolean prune() { + return prune; + } + + /** + * @return The direction in which the tree is traversed. + */ + public int direction() { + return direction; + } + + /** + * Returns true if the traversal traverses in the forward + * direction? + */ + public boolean forward() { + return direction == FORWARD; + } + + public boolean reverse() { + return direction == REVERSE; + } + + public void visitFlowGraph(FlowGraph graph) { + graph.visitChildren(this); + } + + public void visitBlock(Block block) { + block.visitChildren(this); + } + + public void visitTree(Tree tree) { + visitNode(tree); + } + + public void visitExprStmt(ExprStmt stmt) { + visitStmt(stmt); + } + + public void visitIfStmt(IfStmt stmt) { + visitStmt(stmt); + } + + public void visitIfCmpStmt(IfCmpStmt stmt) { + visitIfStmt(stmt); + } + + public void visitIfZeroStmt(IfZeroStmt stmt) { + visitIfStmt(stmt); + } + + public void visitInitStmt(InitStmt stmt) { + visitStmt(stmt); + } + + public void visitGotoStmt(GotoStmt stmt) { + visitStmt(stmt); + } + + public void visitLabelStmt(LabelStmt stmt) { + visitStmt(stmt); + } + + public void visitMonitorStmt(MonitorStmt stmt) { + visitStmt(stmt); + } + + public void visitPhiStmt(PhiStmt stmt) { + visitStmt(stmt); + } + + public void visitCatchExpr(CatchExpr expr) { + visitExpr(expr); + } + + public void visitDefExpr(DefExpr expr) { + visitExpr(expr); + } + + public void visitStackManipStmt(StackManipStmt stmt) { + visitStmt(stmt); + } + + public void visitPhiCatchStmt(PhiCatchStmt stmt) { + visitPhiStmt(stmt); + } + + public void visitPhiJoinStmt(PhiJoinStmt stmt) { + visitPhiStmt(stmt); + } + + public void visitRetStmt(RetStmt stmt) { + visitStmt(stmt); + } + + public void visitReturnExprStmt(ReturnExprStmt stmt) { + visitStmt(stmt); + } + + public void visitReturnStmt(ReturnStmt stmt) { + visitStmt(stmt); + } + + public void visitAddressStoreStmt(AddressStoreStmt stmt) { + visitStmt(stmt); + } + + public void visitStoreExpr(StoreExpr expr) { + visitExpr(expr); + } + + public void visitJsrStmt(JsrStmt stmt) { + visitStmt(stmt); + } + + public void visitSwitchStmt(SwitchStmt stmt) { + visitStmt(stmt); + } + + public void visitThrowStmt(ThrowStmt stmt) { + visitStmt(stmt); + } + + public void visitStmt(Stmt stmt) { + visitNode(stmt); + } + + public void visitSCStmt(SCStmt stmt) { + visitStmt(stmt); + } + + public void visitSRStmt(SRStmt stmt) { + visitStmt(stmt); + } + + public void visitArithExpr(ArithExpr expr) { + visitExpr(expr); + } + + public void visitArrayLengthExpr(ArrayLengthExpr expr) { + visitExpr(expr); + } + + public void visitMemExpr(MemExpr expr) { + visitDefExpr(expr); + } + + public void visitMemRefExpr(MemRefExpr expr) { + visitMemExpr(expr); + } + + public void visitArrayRefExpr(ArrayRefExpr expr) { + visitMemRefExpr(expr); + } + + public void visitCallExpr(CallExpr expr) { + visitExpr(expr); + } + + public void visitCallMethodExpr(CallMethodExpr expr) { + visitCallExpr(expr); + } + + public void visitCallStaticExpr(CallStaticExpr expr) { + visitCallExpr(expr); + } + + public void visitCastExpr(CastExpr expr) { + visitExpr(expr); + } + + public void visitConstantExpr(ConstantExpr expr) { + visitExpr(expr); + } + + public void visitFieldExpr(FieldExpr expr) { + visitMemRefExpr(expr); + } + + public void visitInstanceOfExpr(InstanceOfExpr expr) { + visitExpr(expr); + } + + public void visitLocalExpr(LocalExpr expr) { + visitVarExpr(expr); + } + + public void visitNegExpr(NegExpr expr) { + visitExpr(expr); + } + + public void visitNewArrayExpr(NewArrayExpr expr) { + visitExpr(expr); + } + + public void visitNewExpr(NewExpr expr) { + visitExpr(expr); + } + + public void visitNewMultiArrayExpr(NewMultiArrayExpr expr) { + visitExpr(expr); + } + + public void visitCheckExpr(CheckExpr expr) { + visitExpr(expr); + } + + public void visitZeroCheckExpr(ZeroCheckExpr expr) { + visitCheckExpr(expr); + } + + public void visitRCExpr(RCExpr expr) { + visitCheckExpr(expr); + } + + public void visitUCExpr(UCExpr expr) { + visitCheckExpr(expr); + } + + public void visitReturnAddressExpr(ReturnAddressExpr expr) { + visitExpr(expr); + } + + public void visitShiftExpr(ShiftExpr expr) { + visitExpr(expr); + } + + public void visitStackExpr(StackExpr expr) { + visitVarExpr(expr); + } + + public void visitVarExpr(VarExpr expr) { + visitMemExpr(expr); + } + + public void visitStaticFieldExpr(StaticFieldExpr expr) { + visitMemRefExpr(expr); + } + + public void visitExpr(Expr expr) { + visitNode(expr); + } + + public void visitNode(Node node) { + node.visitChildren(this); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/Type0Visitor.java b/src/edu/purdue/cs/bloat/tree/Type0Visitor.java new file mode 100644 index 0000000..cd942d4 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Type0Visitor.java @@ -0,0 +1,148 @@ +/* + * Class: Type0Visitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * Type0Visitor searches up the tree, starting at a LocalExpr, looking for an + * earlier instance of the same definition of that LocalExpr in a Type 0 + * relation. + * + * @author Thomas VanDrunen + */ +public class Type0Visitor extends AscendVisitor { + + boolean found; // have we found an earlier occurence + + static boolean DEBUG = false; + + public Type0Visitor(HashMap defInfoMap, HashMap useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public boolean search(LocalExpr start) { + this.start = start; + previous = this.start; + found = false; + this.start.parent().visit(this); + return found; + } + + public void check(Node node) { + + if (node instanceof ExprStmt) // if it's an ExprStmt, the expr + check(((ExprStmt) node).expr()); // might be something we want + + // the next conditional should be true if the node is a + // Stmt but not an ExprStmt OR if it is an ExprStmt but the + // above thing didn't find a match + + if (!found && node instanceof Stmt) { + found = (new Type0DownVisitor(useInfoMap, defInfoMap)).search(node, + start); + } + + else if (node instanceof StoreExpr) { // if it's a StoreExpr, we need + StoreExpr n = (StoreExpr) node; // to see if the target matches + + if (n.target() instanceof LocalExpr // this funny condition + && n.expr() instanceof LocalExpr // weeds out moves + // between + && (((LocalExpr) n.target()).index() // identically + // colored + == ((LocalExpr) n.expr()).index())) + ; // local vars + // bloat will ignore these later, and we don't want to + // depend on them as a source of stack stuff + else + check(n.target()); + } + + else if (node instanceof InitStmt) { // if it's an InitStmt, + LocalExpr[] targets = ((InitStmt) node).targets(); // check the + // last + if (targets.length > 0) // initialization + check(targets[targets.length - 1]); + } + + // if it's a LocalExpr... + else if (node instanceof LocalExpr) { + if (((LocalExpr) node).index() == start.index() // compare index + && ((LocalExpr) node).def() == start.def()) { // and + // def + // we've found a match + // update information + + ((UseInformation) useInfoMap.get(start)).type = 0; + ((UseInformation) useInfoMap.get(node)).type0s++; + found = true; + } + } + + } + +} + +class Type0DownVisitor extends DescendVisitor { + + public Type0DownVisitor(HashMap useInfoMap, HashMap defInfoMap) { + super(useInfoMap, defInfoMap); + } + + public void visitLocalExpr(LocalExpr expr) { + if (expr.index() == start.index() && expr.def() == start.def()) { + // we've found a match + // update information + ((UseInformation) useInfoMap.get(start)).type = 0; + UseInformation ui = (UseInformation) useInfoMap.get(expr); + ui.type0s++; + if (exchangeFactor == 1) { + ui.type0_x1s++; + + } + if (exchangeFactor == 2) + ui.type0_x2s++; + + found = true; + } + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/Type1Visitor.java b/src/edu/purdue/cs/bloat/tree/Type1Visitor.java new file mode 100644 index 0000000..afbeedc --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/Type1Visitor.java @@ -0,0 +1,186 @@ +/* + * Class: Type1Visitor + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import java.util.*; + +/** + * Type1Visitor... + */ +public class Type1Visitor extends AscendVisitor { + + Node turningPoint; /* + * where we were when we started descending the tree + * instead of ascending + */ + + boolean found; /* have we found an earlier occurence */ + + public Type1Visitor(HashMap defInfoMap, HashMap useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public void search(LocalExpr start) { + + this.start = start; + previous = this.start; + found = false; + start.parent().visit(this); + if (!found) { + + if (turningPoint != null) { + (new Type1UpVisitor(defInfoMap, useInfoMap)).search( + turningPoint, start); + } else { // search failed (one place I saw this was in + // a ZeroCheckExpression) + + ((DefInformation) defInfoMap.get(start.def())).type1s += 3; + } + + } + + } + + public void check(Node node) { + + if (node instanceof Expr && ((Expr) node).type().getSize() == 2) { + turningPoint = null; + return; // give up. We cannot swap around a wide value. + } + + turningPoint = node; + + if (node instanceof StoreExpr) // if it's a StoreExpr, we need + check(((StoreExpr) node).expr()); // to search down the expression + // being stored + + // if it's a LocalExpr or Stmt, Type0Visitor would have inspected it. + // otherwise... + else if (!(node instanceof LocalExpr) && node instanceof Expr) + + found = (new Type1DownVisitor(useInfoMap, defInfoMap)).search(node, + start); + + } +} + +class Type1UpVisitor extends AscendVisitor { + + Node turningPoint; + + boolean found; + + Type1UpVisitor(HashMap defInfoMap, HashMap useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public void search(Node turningPoint, LocalExpr start) { + + found = false; + this.start = start; + previous = turningPoint; + this.turningPoint = turningPoint; + if (turningPoint.parent() != null + && !(turningPoint.parent() instanceof Tree)) // we can't + // go more than one statement earlier + // because we don't know if the intermediate + // statment leaves anything on the stack. + turningPoint.parent().visit(this); + + if (!found) { + // if we've found nothing by now, we won't find anything. + // setting the type1s of the definition to something 3 or + // greater insures the variable will be stored + ((DefInformation) defInfoMap.get(start.def())).type1s += 3; + } + + } + + public void check(Node node) { + + if (node instanceof ExprStmt) // if it's an ExprStmt, the expr + check(((ExprStmt) node).expr()); // might be something we want + + else if (node instanceof StoreExpr) // if it's a StoreExpr, we need + check(((StoreExpr) node).target()); // to see if the target matches + + // if it's a LocalExpr... + else if (node instanceof LocalExpr + && ((LocalExpr) node).index() == start.index() // compare index + && ((LocalExpr) node).def() == start.def()) { // and def + // we've found a match + // update information + ((UseInformation) useInfoMap.get(start)).type = 1; + ((UseInformation) useInfoMap.get(node)).type1s++; + ((DefInformation) defInfoMap.get(start.def())).type1s++; + found = true; + return; + } + + } + +} + +class Type1DownVisitor extends DescendVisitor { + + public Type1DownVisitor(HashMap useInfoMap, HashMap defInfoMap) { + super(useInfoMap, defInfoMap); + } + + public void visitLocalExpr(LocalExpr expr) { + + if (expr.index() == start.index() && expr.def() == start.def()) { + // we've found a match + // update information + ((UseInformation) useInfoMap.get(start)).type = 1; + UseInformation ui = (UseInformation) useInfoMap.get(expr); + ui.type1s++; + if (exchangeFactor == 1) { + ui.type1_x1s++; + } + if (exchangeFactor == 2) + ui.type1_x2s++; + // System.err.println(expr.toString()); + ((DefInformation) defInfoMap.get(expr.def())).type1s++; + found = true; + } + } + +} diff --git a/src/edu/purdue/cs/bloat/tree/UCExpr.java b/src/edu/purdue/cs/bloat/tree/UCExpr.java new file mode 100644 index 0000000..82b0b66 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/UCExpr.java @@ -0,0 +1,86 @@ +/* + * Class: UCExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * UPExpr represents an update check opcode which checks the persistent store to + * determine if a variable needs to be updated. + */ +public class UCExpr extends CheckExpr { + public static final int POINTER = 1; + + public static final int SCALAR = 2; + + final int kind; + + /** + * Constructor. + * + * @param expr + * The expression to check to see if it needs to be updated. + * @param kind + * The kind of expression (POINTER or SCALAR) to be checked. + * @param type + * The type of this expression. + */ + public UCExpr(Expr expr, int kind, Type type) { + super(expr, type); + this.kind = kind; + } + + public int kind() { + return kind; + } + + public void visit(TreeVisitor visitor) { + visitor.visitUCExpr(this); + } + + public boolean equalsExpr(Expr other) { + return other instanceof UCExpr && super.equalsExpr(other) + && ((UCExpr) other).kind == kind; + } + + public Object clone() { + return copyInto(new UCExpr((Expr) expr.clone(), kind, type)); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/UseInformation.java b/src/edu/purdue/cs/bloat/tree/UseInformation.java new file mode 100644 index 0000000..fca8f8d --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/UseInformation.java @@ -0,0 +1,73 @@ +/* + * Class: UseInformation + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +/** + * UseInformation stores information about a use of a local variable. + * + * @author Thomas VanDrunen + */ +public class UseInformation { + + int type; + + int type0s; + + int type1s; + + int type0_x1s; + + int type0_x2s; + + int type1_x1s; + + int type1_x2s; + + public UseInformation() { + + type = 2; // assume type > 1 unless discovered otherwise + type0s = 0; + type1s = 0; + type0_x1s = 0; + type0_x2s = 0; + type1_x1s = 0; + type1_x2s = 0; + } +} diff --git a/src/edu/purdue/cs/bloat/tree/VarExpr.java b/src/edu/purdue/cs/bloat/tree/VarExpr.java new file mode 100644 index 0000000..8ecca02 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/VarExpr.java @@ -0,0 +1,89 @@ +/* + * Class: VarExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * VarExpr represents an expression that accesses a local variable or a variable + * on the stack. + * + * @see StackExpr + * @see LocalExpr + * + * @see DefExpr + */ +public abstract class VarExpr extends MemExpr { + int index; + + /** + * Constructor. + * + * @param index + * Index giving location of expression. For instance, the number + * local variable represented or the position of the stack + * variable represented. + * @param type + * Type (descriptor) of this expression. + */ + public VarExpr(int index, Type type) { + super(type); + this.index = index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int index() { + return index; + } + + /** + * Returns the expression that defines this expression. + */ + public DefExpr def() { + if (isDef()) { + return this; + } + + return super.def(); + } +} diff --git a/src/edu/purdue/cs/bloat/tree/ZeroCheckExpr.java b/src/edu/purdue/cs/bloat/tree/ZeroCheckExpr.java new file mode 100644 index 0000000..ccddb41 --- /dev/null +++ b/src/edu/purdue/cs/bloat/tree/ZeroCheckExpr.java @@ -0,0 +1,75 @@ +/* + * Class: ZeroCheckExpr + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.tree; + +import org.apache.bcel.generic.Type; + +/** + * ZeroCheckExpr represents a check for a zero value. For instance, when a + * division operation is performed. a ZeroCheckExpr is inserted to ensure that + * the divisor is not zero. It is used when division is performed (idiv, + * ldiv) a remainder is taken (irem, lrem), or a field + * is accessed (getfield, putfield + + +

      The classes necessary to create and represent a Java method as an +expression tree. The nodes of the tree model various operations +(arithmetic operations, method calls, exceptions being thrown, etc.) +performed by a method. There are also classes that let you visit and +operate on the tree.

      + + + \ No newline at end of file diff --git a/src/edu/purdue/cs/bloat/util/Assert.java b/src/edu/purdue/cs/bloat/util/Assert.java new file mode 100644 index 0000000..5499b51 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/Assert.java @@ -0,0 +1,127 @@ +/* + * Class: Assert + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import org.apache.bcel.generic.*; +import org.apache.bcel.Constants; + +/** + * Mechanism for making assertions about things in BLOAT. If an assertion fails, + * an IllegalArgumentException is thrown. + */ +public abstract class Assert { + public static void isTrue(boolean test, String msg) { + if (!test) { + // try{ + throw new IllegalArgumentException("Assert.isTrue: " + msg); + // }catch(IllegalArgumentException iae){ + // iae.printStackTrace(); + // throw iae; + // } + } + } + + public static void isFalse(boolean test, String msg) { + if (test) { + throw new IllegalArgumentException("Assert.isFalse: " + msg); + } + } + + public static void isNotNull(Object test, String msg) { + if (test == null) { + throw new IllegalArgumentException("Assert.isNotNull: " + msg); + } + } + + public static void isNull(Object test, String msg) { + if (test != null) { + throw new IllegalArgumentException("Assert.isNull: " + msg); + } + } + + public static void isTrue(boolean test) { + if (!test) { + throw new IllegalArgumentException("Assert.isTrue failed"); + } + } + + public static void isFalse(boolean test) { + if (test) { + throw new IllegalArgumentException("Assert.isFalse failed"); + } + } + + public static void isNotNull(Object test) { + if (test == null) { + throw new IllegalArgumentException("Assert.isNotNull failed"); + } + } + + public static void isNull(Object test) { + if (test != null) { + throw new IllegalArgumentException("Assert.isNull failed"); + } + } + + /** + * A method I couldn't find a BCEL version of if the type will fit into an + * integer it returns Type.INT, if the type is a subclass of object (ie not + * a basic type) it returns Type.OBJECT, otherwise it returns the type + * given. + */ + public static Type simple(Type type) { + if (isIntegral(type)) + return Type.INT; + if (type instanceof ReferenceType) + return Type.OBJECT; + return type; + } + + /** + * A method I couldn't find a BCEL version of, returns true if the type will + * fit into an integer false otherwise. + */ + public static boolean isIntegral(Type type) { + byte t = type.getType(); + return ((t == Constants.T_INT) || (t == Constants.T_CHAR) + || (t == Constants.T_SHORT) || (t == Constants.T_BOOLEAN) || (t == Constants.T_BYTE)); + } + +} diff --git a/src/edu/purdue/cs/bloat/util/Graph.java b/src/edu/purdue/cs/bloat/util/Graph.java new file mode 100644 index 0000000..5308415 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/Graph.java @@ -0,0 +1,976 @@ +/* + * Class: Graph + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * Graph represents a graph of nodes with directed edges between them. + * GraphNodes are created and are added to the Graph before the edges can be + * constructed. Each GraphNode as a unique key associated with it. For instance, + * if a Graph represents a control flow graph, each GraphNode would be + * associated with a basic block. + * + * @see #addNode + * @see #addEdge + * + * @see GraphNode + * @see edu.purdue.cs.bloat.cfg.FlowGraph + */ +public class Graph { + private NodeMap nodes; // The nodes in this Graph + + private NodeList preOrder; // + + private NodeList postOrder; + + private Collection roots; // The root nodes of this Graph (no + + // predacessors) + + private Collection revRoots; // Reverse root nodes of this Graph (no + + // successors) + + // These counts are used to determine when certain actions (such as + // updating the + // roots Collection) should or should not be performed. + protected int rootEdgeModCount = 0; + + protected int revRootEdgeModCount = 0; + + protected int nodeModCount = 0; // Number of nodes that have been + + // modified + + protected int edgeModCount = 0; // Number of edges that have been + + // modified + + protected int removingNode = 0; + + protected int removingEdge = 0; + + /** + * Constructor. + */ + public Graph() { + nodes = new NodeMap(); + preOrder = null; + postOrder = null; + roots = null; + revRoots = null; + } + + /** + * @return The roots of this Graph. That is, the nodes with no predacessors. + */ + public Collection roots() { + if (roots == null || rootEdgeModCount != edgeModCount) { + rootEdgeModCount = edgeModCount; + roots = new ArrayList(); + buildRootList(roots, false); + } + + return roots; + } + + /** + * @return The reverse roots of this Graph. That is, the nodes with no + * successors. + */ + public Collection reverseRoots() { + if (roots == null || revRootEdgeModCount != edgeModCount) { + revRootEdgeModCount = edgeModCount; + revRoots = new ArrayList(); + buildRootList(revRoots, true); + } + + return revRoots; + } + + /** + * Compile a collection of root nodes in this Graph. If reverse is true, the + * collection will contain those nodes with no predacessor nodes. If reverse + * is false, the collection will contain those nodes with no sucessor nodes. + * + * @param c + * A Collection that will contain the roots in the graph. + * @param reverse + * Do we make a reverse traversal of the graph? + */ + private void buildRootList(Collection c, boolean reverse) { + HashSet visited = new LinkedHashSet(nodes.size() * 2); + ArrayList stack = new ArrayList(); + + Iterator iter = nodes.values().iterator(); + + while (iter.hasNext()) { + GraphNode node = (GraphNode) iter.next(); + + if (!visited.contains(node)) { + visited.add(node); + stack.add(node); + + while (!stack.isEmpty()) { + GraphNode v = (GraphNode) stack.remove(stack.size() - 1); + boolean pushed = false; + + Iterator preds = reverse ? v.succs.iterator() : v.preds + .iterator(); + + while (preds.hasNext()) { + GraphNode w = (GraphNode) preds.next(); + + if (!visited.contains(w)) { + visited.add(w); + stack.add(w); + pushed = true; + } + } + + if (!pushed) { + c.add(v); + } + } + } + } + } + + /** + * Return the successors of a given node. + */ + public Collection succs(GraphNode v) { + return new EdgeSet(v, v.succs); + } + + /** + * Returns the predacessors of a given node. + */ + public Collection preds(GraphNode v) { + return new EdgeSet(v, v.preds); + } + + /** + * Determines whether or not a node v is an ancestor (has a lower pre-order + * index and a higher post-order index) of a node w. + * + * @param v + * Candidate ancestor node. + * @param w + * Candidate descendent node. + * + * @return True, if v is an ancestor of w. + */ + public boolean isAncestorToDescendent(GraphNode v, GraphNode w) { + return preOrderIndex(v) <= preOrderIndex(w) + && postOrderIndex(w) <= postOrderIndex(v); + } + + /** + * Returns the index of a given node in a pre-order ordering of this Graph. + */ + public int preOrderIndex(GraphNode node) { + if (preOrder == null || edgeModCount != preOrder.edgeModCount) { + buildLists(); + } + + return node.preOrderIndex(); + } + + /** + * Returns the index of a given node in a post-order ordering of this Graph. + */ + public int postOrderIndex(GraphNode node) { + if (postOrder == null || edgeModCount != postOrder.edgeModCount) { + buildLists(); + } + + return node.postOrderIndex(); + } + + /** + * Returns the nodes in this Graph ordered by their pre-order index. + */ + public List preOrder() { + if (preOrder == null || edgeModCount != preOrder.edgeModCount) { + buildLists(); + } + + return preOrder; + } + + /** + * Return the nodes in this Graph ordered by their post-order index. + */ + public List postOrder() { + if (postOrder == null || edgeModCount != postOrder.edgeModCount) { + buildLists(); + } + + return postOrder; + } + + /** + * Constructs lists of nodes in both pre-order and post-order order. + */ + private void buildLists() { + Iterator iter = roots().iterator(); + + preOrder = new NodeList(); + postOrder = new NodeList(); + + Set visited = new LinkedHashSet(); + + // Calculate the indices of the nodes. + while (iter.hasNext()) { + GraphNode root = (GraphNode) iter.next(); + + Assert.isTrue(nodes.containsValue(root), "Graph does not contain " + + root); + + number(root, visited); + } + + // Mark all nodes that were not numbered as having an index of -1. This + // information is used when removing unreachable nodes. + iter = nodes.values().iterator(); + + while (iter.hasNext()) { + GraphNode node = (GraphNode) iter.next(); + + if (!visited.contains(node)) { + node.setPreOrderIndex(-1); + node.setPostOrderIndex(-1); + } else { + Assert.isTrue(node.preOrderIndex() >= 0); + Assert.isTrue(node.postOrderIndex() >= 0); + } + } + } + + /** + * Removes all nodes from this Graph that cannot be reached in a pre-order + * traversal of the Graph. These nodes have a pre-order index of -1. + */ + public void removeUnreachable() { + if (preOrder == null || edgeModCount != preOrder.edgeModCount) { + buildLists(); + } + + Iterator iter = nodes.entrySet().iterator(); + + while (iter.hasNext()) { + Map.Entry e = (Map.Entry) iter.next(); + + GraphNode v = (GraphNode) e.getValue(); + + if (v.preOrderIndex() == -1) { + iter.remove(); + } + } + } + + /** + * Sets the pre-order and post-order indices of a node. + * + * @param node + * The node to number. + * @param visited + * The nodes that have been visited already. + */ + private void number(GraphNode node, Set visited) { + visited.add(node); + + // Visit in pre-order + node.setPreOrderIndex(preOrder.size()); + preOrder.addNode(node); + + Iterator iter = succs(node).iterator(); + + while (iter.hasNext()) { + GraphNode succ = (GraphNode) iter.next(); + if (!visited.contains(succ)) { + number(succ, visited); + } + } + + // Visit in post-order + node.setPostOrderIndex(postOrder.size()); + postOrder.addNode(node); + } + + /** + * Insertes a node (and its associated key) into this Graph. + * + * @param key + * A unique value associated with this node. For instance, if + * this Graph represented a control flow graph, the key would be + * a basic block. + * @param node + * The node to be added. + */ + // This method is NOT guaranteed to be called whenever a node is added. + public void addNode(Object key, GraphNode node) { + Assert.isTrue(nodes.get(key) == null); + nodes.putNodeInMap(key, node); + preOrder = null; + postOrder = null; + nodeModCount++; + edgeModCount++; + } + + /** + * Returns the node in this Graph with a given key. + */ + public GraphNode getNode(Object key) { + return (GraphNode) nodes.get(key); + } + + /** + * Returns a Set of the keys used to uniquely identify the nodes in this + * Graph. + */ + public Set keySet() { + return nodes.keySet(); + } + + /** + * Removes a node with a given key from the Graph. + * + * @param key + * The key associated with the node to remove. + */ + // This method is guaranteed to be called whenever a node is deleted. + // If removingNode != 0, the node is NOT deleted when this method + // returns. + // It is the callers responsibility to delete the node AFTER this method + // is called. An exception will be thrown if the node is not present + // in the graph. + public void removeNode(Object key) { + GraphNode node = getNode(key); + Assert.isTrue(node != null, "No node for " + key); + + succs(node).clear(); + preds(node).clear(); + + if (removingNode == 0) { + nodes.removeNodeFromMap(key); + } else if (removingNode != 1) { + throw new RuntimeException(); + } + + // Removing a node invalidates the orderings + preOrder = null; + postOrder = null; + + nodeModCount++; + edgeModCount++; + } + + /** + * Adds a directed edge from node v to node w. + * + * @param v + * Source node. + * @param w + * Destination node. + */ + // This method is NOT guaranteed to be called whenever an edge is added. + public void addEdge(GraphNode v, GraphNode w) { + Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v); + Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w); + + succs(v).add(w); + edgeModCount++; + } + + // This method is guaranteed to be called whenever an edge is deleted. + // If removingEdge != 0, the edge is NOT deleted when this method + // returns. + // It is the callers responsibility to delete the edge AFTER this method + // is called. An exception will be thrown if the edge is not present + // in the graph. + public void removeEdge(GraphNode v, GraphNode w) { + Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v); + Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w); + Assert.isTrue(v.succs().contains(w)); + + if (removingEdge == 0) { + succs(v).remove(w); + } else if (removingEdge != 1) { + throw new RuntimeException(); + } + + edgeModCount++; + } + + public String toString() { + String s = ""; + + Iterator iter = nodes.values().iterator(); + + while (iter.hasNext()) { + GraphNode node = (GraphNode) iter.next(); + s += "[" + node; + s += " succs = " + node.succs(); + s += " preds = " + node.preds(); + s += "]\n"; + } + + return s; + } + + /** + * Searchs this Graph for a given GraphNode. + * + * @param v + * GraphNode to search for. + * + * @return True, if this Graphs contains v. + */ + public boolean hasNode(GraphNode v) { + return nodes.containsValue(v); + } + + /** + * Searches this Graph for an (directed) edge between two GraphNodes. + * + * @param v + * Source node of desired edge. + * @param w + * Destination node of desired edge. + * + * @return True, if an edge exists between nodes v and w. + */ + public boolean hasEdge(GraphNode v, GraphNode w) { + Assert.isTrue(nodes.containsValue(v), "Graph does not contain " + v); + Assert.isTrue(nodes.containsValue(w), "Graph does not contain " + w); + return succs(v).contains(w); + } + + /** + * Returns all the nodes in this Graph. + */ + public Collection nodes() { + return nodes.values(); + } + + /** + * Returns the number of nodes in this Graph. + */ + public int size() { + return nodes.size(); + } + + /** + * A NodeMap that stores nodes. I guess we use this data structure to make + * it easier to ensure that there are not duplicate nodes in the Graph. A + * HashMap is used as the underlying stored mechanism. + */ + class NodeMap extends AbstractMap { + Map map = new LinkedHashMap(); + + void removeNodeFromMap(Object key) { + map.remove(key); + } + + void putNodeInMap(Object key, Object value) { + map.put(key, value); + } + + public Object remove(Object key) { + GraphNode v = (GraphNode) map.get(key); + + if (v != null) { + Graph.this.removeNode(v); + } + + return v; + } + + public Object put(Object key, Object value) { + GraphNode v = (GraphNode) remove(key); + Graph.this.addNode(key, (GraphNode) value); + return v; + } + + public void clear() { + Iterator iter = entrySet().iterator(); + + while (iter.hasNext()) { + Map.Entry e = (Map.Entry) iter.next(); + removingNode++; + Graph.this.removeNode(e.getKey()); + removingNode--; + iter.remove(); + } + } + + public Set entrySet() /* Modified for final JDK1.2 API */ + { + final Collection entries = map.entrySet(); + + return new AbstractSet() { + public int size() { + return entries.size(); + } + + public boolean contains(Object a) { + return entries.contains(a); + } + + public boolean remove(Object a) { + Map.Entry e = (Map.Entry) a; + + removingNode++; + Graph.this.removeNode(e.getKey()); + removingNode--; + + return entries.remove(a); + } + + public void clear() { + Iterator iter = entries.iterator(); + + while (iter.hasNext()) { + Map.Entry e = (Map.Entry) iter.next(); + removingNode++; + Graph.this.removeNode(e.getKey()); + removingNode--; + iter.remove(); + } + } + + public Iterator iterator() { + final Iterator iter = entries.iterator(); + + return new Iterator() { + int nodeModCount = Graph.this.nodeModCount; + + Map.Entry last; + + public boolean hasNext() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + return iter.hasNext(); + } + + public Object next() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + last = (Map.Entry) iter.next(); + return last; + } + + public void remove() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + removingNode++; + Graph.this.removeNode(last.getKey()); + removingNode--; + iter.remove(); + + nodeModCount = Graph.this.nodeModCount; + } + }; + } + }; + } + } + + /** + * NodeList represents a list of nodes. Special provisions must be made for + * methods such as indexOf() and iterator(). A NodeList is used to store the + * pre-order and post-order travsersals of the Graph. + */ + class NodeList extends ArrayList implements List { + static final long serialVersionUID = 1L; + + int edgeModCount; + + NodeList() { + super(Graph.this.size()); + edgeModCount = Graph.this.edgeModCount; + } + + boolean addNode(GraphNode a) { + return super.add(a); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean add(Object a) { + throw new UnsupportedOperationException(); + } + + public boolean remove(Object a) { + throw new UnsupportedOperationException(); + } + + // This works only if each node is in the list at most once. + public int indexOf(Object a) { + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + GraphNode v = (GraphNode) a; + + if (this == Graph.this.preOrder) { + return v.preOrderIndex(); + } + + if (this == Graph.this.postOrder) { + return v.postOrderIndex(); + } + + return super.indexOf(a); + } + + // This works only if each node is in the list at most once. + public int indexOf(Object a, int index) { + int i = indexOf(a); + + if (i >= index) { + return i; + } + + return -1; + } + + // This works only if each node is in the list at most once. + public int lastIndexOf(Object a) { + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + GraphNode v = (GraphNode) a; + + if (this == Graph.this.preOrder) { + return v.preOrderIndex(); + } + + if (this == Graph.this.postOrder) { + return v.postOrderIndex(); + } + + return super.lastIndexOf(a); + } + + // This works only if each node is in the list at most once. + public int lastIndexOf(Object a, int index) { + int i = indexOf(a); + + if (i <= index) { + return i; + } + + return -1; + } + + public Iterator iterator() { + if (Graph.this.edgeModCount != edgeModCount) { + throw new ConcurrentModificationException(); + } + + final Iterator iter = super.iterator(); + + return new Iterator() { + int edgeModCount = NodeList.this.edgeModCount; + + Object last; + + public boolean hasNext() { + if (Graph.this.edgeModCount != edgeModCount) { + throw new ConcurrentModificationException(); + } + + return iter.hasNext(); + } + + public Object next() { + if (Graph.this.edgeModCount != edgeModCount) { + throw new ConcurrentModificationException(); + } + + last = iter.next(); + return last; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + /** + * A set of edges. Recall that a Set cannot contain duplicate entries. + */ + class EdgeSet extends AbstractSet { + GraphNode node; + + Set set; + + int nodeModCount; + + /** + * + */ + public EdgeSet(GraphNode node, Set set) { + this.node = node; + this.set = set; + this.nodeModCount = Graph.this.nodeModCount; + } + + public int size() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + return set.size(); + } + + /** + * Removes all nodes in this set except for those found in collection. + * + * @param c + * Nodes that are to be retained. + */ + public boolean retainAll(Collection c) { + return super.retainAll(new ArrayList(c)); + } + + /** + * Removes all of the nodes in this set that are specified in a given + * Collection. + * + * @param c + * The nodes to remove. + */ + public boolean removeAll(Collection c) { + return super.removeAll(new ArrayList(c)); + } + + /** + * Adds all of the nodes in a Collection to this set. + */ + public boolean addAll(Collection c) { + return super.addAll(new ArrayList(c)); + } + + public boolean add(Object a) { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + Assert.isTrue(nodes.containsValue(a)); + Assert.isTrue(nodes.containsValue(node)); + + GraphNode v = (GraphNode) a; + + if (set.add(v)) { + Graph.this.edgeModCount++; + + if (set == node.succs) { + v.preds.add(node); + } else { + v.succs.add(node); + } + + return true; + } + + return false; + } + + public boolean remove(Object a) { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + GraphNode v = (GraphNode) a; + + if (set.contains(v)) { + Graph.this.edgeModCount++; + + if (set == node.succs) { + removingEdge++; + Graph.this.removeEdge(node, v); + removingEdge--; + v.preds.remove(node); + } else { + removingEdge++; + Graph.this.removeEdge(v, node); + removingEdge--; + v.succs.remove(node); + } + + set.remove(v); + + return true; + } + + return false; + } + + public boolean contains(Object a) { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + Assert.isTrue(nodes.containsValue(a)); + Assert.isTrue(nodes.containsValue(node)); + + if (a instanceof GraphNode) { + return set.contains(a); + } + + return false; + } + + public void clear() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + Iterator iter = set.iterator(); + + while (iter.hasNext()) { + GraphNode v = (GraphNode) iter.next(); + + if (set == node.succs) { + removingEdge++; + Graph.this.removeEdge(node, v); + removingEdge--; + v.preds.remove(node); + } else { + removingEdge++; + Graph.this.removeEdge(v, node); + removingEdge--; + v.succs.remove(node); + } + } + + Graph.this.edgeModCount++; + + set.clear(); + } + + public Iterator iterator() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + final Iterator iter = set.iterator(); + + return new Iterator() { + GraphNode last; + + int edgeModCount = Graph.this.edgeModCount; + + int nodeModCount = EdgeSet.this.nodeModCount; + + public boolean hasNext() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + return iter.hasNext(); + } + + public Object next() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + last = (GraphNode) iter.next(); + + Assert.isTrue(nodes.containsValue(last), last + + " not found in graph"); + Assert.isTrue(nodes.containsValue(node), node + + " not found in graph"); + + return last; + } + + public void remove() { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + if (set == node.succs) { + removingEdge++; + Graph.this.removeEdge(node, last); + removingEdge--; + last.preds.remove(node); + } else { + removingEdge++; + Graph.this.removeEdge(last, node); + removingEdge--; + last.succs.remove(node); + } + + Graph.this.edgeModCount++; + edgeModCount = Graph.this.edgeModCount; + + iter.remove(); + } + }; + } + } +} diff --git a/src/edu/purdue/cs/bloat/util/GraphNode.java b/src/edu/purdue/cs/bloat/util/GraphNode.java new file mode 100644 index 0000000..502e0d6 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/GraphNode.java @@ -0,0 +1,108 @@ +/* + * Class: GraphNode + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * GraphNode represents a node in a Graph. Each node has a set of predacessors + * and successors associated with it as well as a pre-order and post-order + * traversal index. This information is maintained by the Graph in which the + * GraphNode resides. + * + * @see Graph + */ +public abstract class GraphNode { + protected Set succs; + + protected Set preds; + + protected int preIndex; + + protected int postIndex; + + /** + * Constructor. + */ + public GraphNode() { + succs = new LinkedHashSet(); + preds = new LinkedHashSet(); + preIndex = -1; + postIndex = -1; + } + + /** + * @return This node's index in a pre-order traversal of the Graph that + * contains it. + */ + int preOrderIndex() { + return preIndex; + } + + /** + * @return This node's index in a post-order traversal of the Graph that + * contains it. + */ + int postOrderIndex() { + return postIndex; + } + + void setPreOrderIndex(int index) { + preIndex = index; + } + + void setPostOrderIndex(int index) { + postIndex = index; + } + + /** + * Returns the successor (or children) nodes of this GraphNode. + */ + protected Collection succs() { + return succs; + } + + /** + * Returns the predacessor (or parent) nodes of this GraphNode. + */ + protected Collection preds() { + return preds; + } +} diff --git a/src/edu/purdue/cs/bloat/util/IdentityComparator.java b/src/edu/purdue/cs/bloat/util/IdentityComparator.java new file mode 100644 index 0000000..3f4bf24 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/IdentityComparator.java @@ -0,0 +1,62 @@ +/* + * Class: IdentityComparator + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * IdentityComparator compares two objects using the result of + * System.identityHashCode. + */ +public class IdentityComparator implements Comparator { + public int compare(Object o1, Object o2) { + int n1 = System.identityHashCode(o1); + int n2 = System.identityHashCode(o2); + + return n1 - n2; + /* + * if (n1 > n2) { return 1; } + * + * if (n1 < n2) { return -1; } + * + * return 0; + */ + } +} diff --git a/src/edu/purdue/cs/bloat/util/ImmutableIterator.java b/src/edu/purdue/cs/bloat/util/ImmutableIterator.java new file mode 100644 index 0000000..da22ec4 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/ImmutableIterator.java @@ -0,0 +1,66 @@ +/* + * Class: ImmutableIterator + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * ImmutableIterator is simply an iterator whose contents can not be changed. + * That is, the remove() method has no effect. + */ +public class ImmutableIterator implements Iterator { + Iterator iter; + + public ImmutableIterator(Collection c) { + iter = new ArrayList(c).iterator(); + } + + public Object next() { + return iter.next(); + } + + public boolean hasNext() { + return iter.hasNext(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/edu/purdue/cs/bloat/util/Makefile b/src/edu/purdue/cs/bloat/util/Makefile new file mode 100644 index 0000000..9e62bfa --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/Makefile @@ -0,0 +1,31 @@ +# All files in the distribution of BLOAT (Bytecode Level Optimization and +# Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue +# Research Foundation of Purdue University. All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that this entire copyright notice is duplicated in all such +# copies, and that any documentation, announcements, and other +# materials related to such distribution and use acknowledge that the +# software was developed at Purdue University, West Lafayette, IN by +# Antony Hosking, David Whitlock, and Nathaniel Nystrom. No charge +# may be made for copies, derivations, or distributions of this +# material without the express written consent of the copyright +# holder. Neither the name of the University nor the name of the +# author may be used to endorse or promote products derived from this +# material without specific prior written permission. THIS SOFTWARE +# IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. +# +# Java is a trademark of Sun Microsystems, Inc. + +CLASS = \ + Assert.class\ + Graph.class\ + GraphNode.class\ + IdentityComparator.class\ + ImmutableIterator.class\ + ResizeableArrayList.class\ + UnionFind.class + +include ../class.mk diff --git a/src/edu/purdue/cs/bloat/util/ResizeableArrayList.java b/src/edu/purdue/cs/bloat/util/ResizeableArrayList.java new file mode 100644 index 0000000..1c35592 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/ResizeableArrayList.java @@ -0,0 +1,82 @@ +/* + * Class: ResizeableArrayList + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * ResizableArrayList is the same as ArrayList except that ensureSize not only + * increases the size of the array (super.ensureCapacity), but it also fills the + * empty space with null. This way, the size method will return the length of + * the array and not just the number of elements in it. I guess. + */ +public class ResizeableArrayList extends ArrayList implements List, Cloneable, + java.io.Serializable { + static final long serialVersionUID = 1L; + + /** + * This constructor is no longer supported in JDK1.2 public + * ResizeableArrayList(int initialCapacity, int capacityIncrement) { + * super(initialCapacity, capacityIncrement); } + */ + public ResizeableArrayList(int initialCapacity) { + super(initialCapacity); + } + + public ResizeableArrayList() { + super(); + } + + public ResizeableArrayList(Collection c) { + super(c); + } + + public void ensureSize(int size) { + ensureCapacity(size); + + while (size() < size) { + add(null); + } + } + + public Object clone() { + return (ResizeableArrayList) super.clone(); + } +} diff --git a/src/edu/purdue/cs/bloat/util/UnionFind.java b/src/edu/purdue/cs/bloat/util/UnionFind.java new file mode 100644 index 0000000..28be10b --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/UnionFind.java @@ -0,0 +1,197 @@ +/* + * Class: + * Version: + * Date: + * + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + *

      + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all + * such copies, and that any documentation, announcements, and other + * materials related to such distribution and use acknowledge that the + * software was developed at Purdue University, West Lafayette, IN by + * @author Antony Hosking + * @author David Whitlock + * @author Nathaniel Nystrom + * No charge may be made for copies, derivations, or distributions of + * this material without the express written consent of the copyright + * holder. Neither the name of the University nor the name of the + * author may be used to endorse or promote products derived from this + * material without specific prior written permission. THIS SOFTWARE + * IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE. + * + *

      + * + * Port of the BLOAT to use the Jakata Apache project BCEL 5.2 was + * contributed by The Australian National University by + * @author Arrin Daley + * @author John N Zigman + * + *

      + * Java is a trademark of Sun Microsystems, Inc. + */ + +package edu.purdue.cs.bloat.util; + +import java.util.*; + +/** + * Represents the union-find data structure. + * + *

      + * + * Sometimes we need to group elements into disjoint sets. Two important + * operations of these sets are finding the set that contains a given element + * ("find") and uniting two sets ("union"). UnionFind provides an + * efficient implementation of a data structure that support these operations on + * disjoint sets of integers. + * + *

      + * + * Each disjoint set is represented by a tree consisting of Nodes. + * (This Node is a class local to UnionFind and should not + * be confused with tree.Node.) Each Node knows its + * parent and child and has a rank associated with it. The parent node is always + * the root node of the set tree. A Node's rank is essentially the + * height of the (sub)tree rooted by that node. When the union of two trees is + * formed, the root with the smaller rank is made to point to the root with the + * larger rank. Naturally, each Node has an integer "value" + * associated with it. + * + *

      + * + * A good description of union-find can be found in [Cormen, et. al. 1990]. + */ +public class UnionFind { + // The trees of Nodes that represent the disjoint sets. + ResizeableArrayList nodes; + + /** + * Constructor. + */ + public UnionFind() { + nodes = new ResizeableArrayList(); + } + + /** + * Constructor. Make a UnionFind with a given number of disjoint + * sets. + */ + public UnionFind(int size) { + nodes = new ResizeableArrayList(size); + } + + /** + * Searches the disjoint sets for a given integer. Returns the set + * containing the integer a. Sets are represented by a local class + * Node. + */ + public Node findNode(int a) { + nodes.ensureSize(a + 1); + + Node na = (Node) nodes.get(a); + + if (na == null) { + // Start a new set with a + Node root = new Node(a); + + root.child = new Node(a); + root.child.parent = root; + + nodes.set(a, root.child); + + return root; + } + + return findNode(na); + } + + /** + * Returns the integer value associated with the first Node in a + * set. + */ + public int find(int a) { + return findNode(a).value; + } + + /** + * Finds the set containing a given Node. + */ + private Node findNode(Node node) { + Stack stack = new Stack(); + + // Find the child of the root element. + while (node.parent.child == null) { + stack.push(node); + node = node.parent; + } + + // Do path compression on the way back down. + Node rootChild = node; + + while (!stack.empty()) { + node = (Node) stack.pop(); + node.parent = rootChild; + } + + Assert.isTrue(rootChild.parent.child != null); + + return rootChild.parent; + } + + /** + * Returns true if a and b are in the same set. + */ + public boolean isEquiv(int a, int b) { + return findNode(a) == findNode(b); + } + + /** + * Combines the set that contains a with the set that contains b. + */ + public void union(int a, int b) { + Node na = findNode(a); + Node nb = findNode(b); + + if (na == nb) { + return; + } + + // Link the smaller tree under the larger. + if (na.rank > nb.rank) { + // Delete nb. + nb.child.parent = na.child; + na.value = b; + + } else { + // Delete na. + na.child.parent = nb.child; + nb.value = b; + + if (na.rank == nb.rank) { + nb.rank++; + } + } + } + + class Node { + Node parent; // The root of the tree in which this Node resides + + Node child; + + int value; + + int rank; // This Node's height in the tree + + public Node(int v) { + value = v; + rank = 0; + } + } +} diff --git a/src/edu/purdue/cs/bloat/util/package.html b/src/edu/purdue/cs/bloat/util/package.html new file mode 100644 index 0000000..352a200 --- /dev/null +++ b/src/edu/purdue/cs/bloat/util/package.html @@ -0,0 +1,9 @@ + + + +

      Contains a number of utility classes used by BLOAT. BLOAT uses +several utility classes such as a directed graph and an immutatable +iterator to get its work done.

      + + + \ No newline at end of file diff --git a/src/overview.html b/src/overview.html new file mode 100644 index 0000000..0732d0e --- /dev/null +++ b/src/overview.html @@ -0,0 +1,26 @@ + + +

      BLOAT, the Bytecode-Level Optimizer and Analysis Tools, is a Java +classfile optimizer that is written entirely in Java. BLOAT was +designed and developed by Nate Nystrom in 1998 and performs a number +of intraprocedural optimizations on Java bytecode:

      + +
        +
      • Control flow graph construction +
      • Conversion to static single assignment (SSA) form +
      • Constant and copy propagation +
      • Dead code elimination +
      • Partial redundency elimination of expressions and access paths + (e.g. array and fieldreferences) +
      • Efficient "register" (JVM local variables) allocation +
      • Java bytecode peephole optimizations +
      + +

      More information about BLOAT can be found at:

      + +

      +http://www.cs.purdue.edu/homes/whitlock/bloat

      + + + \ No newline at end of file