commit f06322c01e99343a1da3cb483b2cd42293cf14d8 Author: patrickroemer Date: Mon Jul 31 11:29:27 2006 +0000 initial upload Bloat 1.0 diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fb50116 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/.cvsignore @@ -0,0 +1 @@ +bin diff --git a/.project b/.project new file mode 100644 index 0000000..94bdc75 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + bloat + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..9b24740 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +#Tue Oct 18 04:56:05 CEST 2005 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1 +org.eclipse.jdt.core.compiler.compliance=1.3 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore +org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore +org.eclipse.jdt.core.compiler.source=1.3 diff --git a/changes.txt b/changes.txt new file mode 100644 index 0000000..f9b4ec0 --- /dev/null +++ b/changes.txt @@ -0,0 +1,7 @@ +- FlowGraph#addHandlerEdges: added visited parameter to keep track of +blocks already visited to break 'infinite loop' in compiler-generated +'self-handling' exception handlers since JDK 1.4 + +- Type#getType(Class): small fix for array type descriptors + +- Tree#visit_ldc: broken hack for JDK5 class constants - check usages and fix \ No newline at end of file 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..1d70388 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/Makefile @@ -0,0 +1,74 @@ +# 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. +# +# 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 + + +SUBDIRS = util \ + reflect \ + file \ + editor \ + inline \ + tree \ + cfg \ + ssa \ + tbaa \ + trans \ + diva \ + codegen \ + context \ + decorate \ + optimize \ + shrink \ + strip \ + dump \ + tools \ + benchmark + +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.file \ + 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.decorate \ + EDU.purdue.cs.bloat.optimize \ + EDU.purdue.cs.bloat.shrink \ + EDU.purdue.cs.bloat.strip \ + EDU.purdue.cs.bloat.dump \ + EDU.purdue.cs.bloat.benchmark diff --git a/src/EDU/purdue/cs/bloat/benchmark/.cvsignore b/src/EDU/purdue/cs/bloat/benchmark/.cvsignore new file mode 100644 index 0000000..b48726e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/.cvsignore @@ -0,0 +1,2 @@ +*.class +EDU_purdue_cs_bloat_benchmark_Benchmark.h \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/benchmark/Benchmark.java b/src/EDU/purdue/cs/bloat/benchmark/Benchmark.java new file mode 100644 index 0000000..4cfb25b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Benchmark.java @@ -0,0 +1,143 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +/** + * This class is used to run a benchmark Java program with Perfmon running in + * the background. Perfmon is a software package developed at Michigan State + * University that allows user-level programs to access the hardware counters on + * Sparc processors. + * + *

+ * + * The main method of this class takes several arguments (note that + * the first four arguments are mutually exclusive): + * + *

+ *    -inst-load-stall       Count load interlock induced stalls
+ *    -dcache                Count data cache hit rate
+ *    -cycle-ic-miss-stall   Count I-cache miss induced stalls (and cycles)
+ *    -inst-cycle            Count instructions (and cycles)
+ * 
+ *    -run n                 How many times is the program run
+ * 
+ *    class                  Java class to run (the benchmark)
+ *    args                   Arguments to benchmark class
+ * 
+ * + * The real work is done by the native run method that is implemented + * in benchmark.c. + * + * @see BenchmarkSecurityManager + */ +public class Benchmark { + static { + // Load native code from libbenchmark.so + System.loadLibrary("benchmark"); + } + + public static native void init(Class main); + + public static native void run(Class main, String[] args); + + public static native void setMode(int mode); + + public static void main(final String[] args) throws Exception { + int mode = 0; + + int runs = 1; + int eat = 0; + + if (args.length <= 1) { + Benchmark.usage(); + } + + for (eat = 0; eat < args.length; eat++) { + if (args[eat].equals("-inst-cycle")) { + mode = 3; + } else if (args[eat].equals("-inst-load-stall")) { + mode = 0; + } else if (args[eat].equals("-dcache")) { + mode = 1; + } else if (args[eat].equals("-cycle-ic-miss-stall")) { + mode = 2; + } else if (args[eat].equals("-run")) { + if (++eat >= args.length) { + Benchmark.usage(); + } + + runs = Integer.parseInt(args[eat]); + + if (runs <= 0) { + Benchmark.usage(); + } + } else { + // The main class + eat++; + break; + } + } + + /* Got all the args. */ + if (eat > args.length) { + Benchmark.usage(); + } + + final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); + System.setSecurityManager(sec); + + final String mainClassName = args[eat - 1]; + final String[] a = new String[args.length - eat]; + + System.err.println("Running " + mainClassName + " in mode " + mode); + Benchmark.setMode(mode); + + final Class mainClass = Class.forName(mainClassName); + Benchmark.init(mainClass); + + for (int i = 0; i < runs; i++) { + try { + System.arraycopy(args, eat, a, 0, a.length); + Benchmark.run(mainClass, a); + } catch (final SecurityException e) { + continue; + } catch (final Exception e) { + e.printStackTrace(System.err); + sec.allowExit = true; + System.exit(1); + } + } + + sec.allowExit = true; + } + + private static void usage() { + System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark "); + System.err.println("options class args..."); + System.err.println("where options are one of:"); + System.err.println(" -inst-load-stall"); + System.err.println(" -inst-cycle"); + System.err.println(" -cycle-ic-miss-stall"); + System.err.println(" -dcache"); + System.err.println(" -run n"); + System.exit(1); + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/BenchmarkSecurityManager.java b/src/EDU/purdue/cs/bloat/benchmark/BenchmarkSecurityManager.java new file mode 100644 index 0000000..191f071 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/BenchmarkSecurityManager.java @@ -0,0 +1,115 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +import java.io.*; + +/** + * The BenchmarkSecurityManager allows us to execute a "main" method + * multiple times without the virtual machine exiting. If exit is not allowed, + * the checkExit method will throw a SecurityException + * that can be caught, thus allowing execution to continue. + * + * @see Shade + * @see Stats + */ +public class BenchmarkSecurityManager extends SecurityManager { + boolean allowExit = false; + + /** + * A SecurityException is thrown if we do not allow the virtual + * machine to exit. + */ + public void checkExit(final int status) { + if (!allowExit) { + System.err.println("exit " + status); + throw new SecurityException("Tried to exit (status=" + status + ")"); + } + } + + public void checkCreateClassLoader() { + } + + public void checkAccess(final Thread t) { + } + + public void checkAccess(final ThreadGroup g) { + } + + public void checkExec(final String cmd) { + } + + public void checkLink(final String lib) { + } + + public void checkRead(final FileDescriptor fd) { + } + + public void checkRead(final String file) { + } + + public void checkRead(final String file, final Object context) { + } + + public void checkWrite(final FileDescriptor fd) { + } + + public void checkWrite(final String file) { + } + + public void checkDelete(final String file) { + } + + public void checkConnect(final String host, final int port) { + } + + public void checkConnect(final String host, final int port, + final Object context) { + } + + public void checkListen(final int port) { + } + + public void checkAccept(final String host, final int port) { + } + + public void checkPropertiesAccess() { + } + + public void checkPropertyAccess(final String key) { + } + + public void checkPropertyAccess(final String key, final String val) { + } + + public boolean checkTopLevelWindow(final Object window) { + return true; + } + + public void checkPackageAccess(final String pkg) { + } + + public void checkPackageDefinition(final String pkg) { + } + + public void checkSetFactory() { + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/CounterDecorate.java b/src/EDU/purdue/cs/bloat/benchmark/CounterDecorate.java new file mode 100644 index 0000000..d2f02f0 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/CounterDecorate.java @@ -0,0 +1,361 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; + +public class CounterDecorate implements Opcode { + private static final String COUNTER_TYPE = "I"; + + private static final String COUNTER_RCNAME = "rcCount"; + + private static final String COUNTER_AUNAME = "auCount"; + + private static final String COUNTER_SUNAME = "suCount"; + + private static final String COUNTER_MAIN = "LEDU/purdue/cs/bloat/benchmark/Counter;"; + + private static int VERBOSE = 0; + + private static boolean FORCE = false; + + private static boolean CLOSURE = false; + + private static final List SKIP = new ArrayList(); + + private static final List ONLY = new ArrayList(); + + public static void main(final String[] args) { + final ClassFileLoader loader = new ClassFileLoader(); + List classes = new ArrayList(); + boolean gotdir = false; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + CounterDecorate.VERBOSE++; + } else if (args[i].equals("-help")) { + CounterDecorate.usage(); + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + CounterDecorate.usage(); + } + + final String classpath = args[i]; + loader.setClassPath(classpath); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + CounterDecorate.usage(); + } + + final String pkg = args[i].replace('.', '/'); + CounterDecorate.SKIP.add(pkg); + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + CounterDecorate.usage(); + } + + final String pkg = args[i].replace('.', '/'); + CounterDecorate.ONLY.add(pkg); + } else if (args[i].equals("-closure")) { + CounterDecorate.CLOSURE = true; + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + } else if (args[i].equals("-f")) { + CounterDecorate.FORCE = true; + } else if (args[i].startsWith("-")) { + CounterDecorate.usage(); + } else if (i == args.length - 1) { + final File f = new File(args[i]); + + if (f.exists() && !f.isDirectory()) { + System.err.println("No such directory: " + f.getPath()); + System.exit(2); + } + + loader.setOutputDir(f); + gotdir = true; + } else { + classes.add(args[i]); + } + } + + if (!gotdir) { + CounterDecorate.usage(); + } + + if (classes.size() == 0) { + CounterDecorate.usage(); + } + + if (CounterDecorate.VERBOSE > 3) { + ClassFileLoader.DEBUG = true; + ClassEditor.DEBUG = true; + } + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + loader.loadClass(name); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + final BloatContext context = new CachingBloatContext(loader, classes, + CounterDecorate.CLOSURE); + + if (!CounterDecorate.CLOSURE) { + final Iterator e = classes.iterator(); + + while (e.hasNext()) { + final String name = (String) e.next(); + try { + final ClassInfo info = loader.loadClass(name); + CounterDecorate.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } else { + classes = null; + + final ClassHierarchy hier = context.getHierarchy(); + + final Iterator e = hier.classes().iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + try { + final ClassInfo info = loader.loadClass(t.className()); + CounterDecorate.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } + } + } + + private static void usage() { + System.err + .println("Usage: java EDU.purdue.cs.bloat.count.Main " + + "\n [-options] classes output_dir" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -v -verbose turn on verbose mode " + + "(can be given multiple times)" + + "\n -classpath " + + "\n list directories in which to look for classes" + + "\n -f decorate files even if up-to-date" + + "\n -closure recursively decorate 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 (this option can be given more than once)" + + "\n -only " + + "\n skip all but the given class or package" + + "\n (this option can be given more than once)"); + System.exit(0); + } + + private static void decorateClass(final EditorContext editor, + final ClassInfo info) { + final ClassFile classFile = (ClassFile) info; + + if (!CounterDecorate.FORCE) { + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (CounterDecorate.VERBOSE > 1) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (CounterDecorate.VERBOSE > 2) { + classFile.print(System.out); + } + + final ClassEditor c = editor.editClass(info); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (CounterDecorate.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < CounterDecorate.ONLY.size(); i++) { + final String pkg = (String) CounterDecorate.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 < CounterDecorate.SKIP.size(); i++) { + final String pkg = (String) CounterDecorate.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (CounterDecorate.VERBOSE > 0) { + System.out.println("Skipping " + c.type().className()); + } + + editor.release(info); + return; + } + + if (CounterDecorate.VERBOSE > 0) { + System.out.println("Decorating class " + c.type().className()); + } + + if (CounterDecorate.VERBOSE > 2) { + ((ClassFile) info).print(System.out); + } + + final MethodInfo[] methods = c.methods(); + + for (int j = 0; j < methods.length; j++) { + MethodEditor m; + + try { + m = editor.editMethod(methods[j]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + CounterDecorate.transform(m); + editor.commit(methods[j]); + } + + editor.commit(info); + } + + private static void transform(final MethodEditor method) { + if (CounterDecorate.VERBOSE > 1) { + System.out.println("Decorating method " + method); + } + + final MemberRef rcfield = new MemberRef(Type + .getType(CounterDecorate.COUNTER_MAIN), new NameAndType( + CounterDecorate.COUNTER_RCNAME, Type + .getType(CounterDecorate.COUNTER_TYPE))); + final MemberRef aufield = new MemberRef(Type + .getType(CounterDecorate.COUNTER_MAIN), new NameAndType( + CounterDecorate.COUNTER_AUNAME, Type + .getType(CounterDecorate.COUNTER_TYPE))); + final MemberRef sufield = new MemberRef(Type + .getType(CounterDecorate.COUNTER_MAIN), new NameAndType( + CounterDecorate.COUNTER_SUNAME, Type + .getType(CounterDecorate.COUNTER_TYPE))); + + final ListIterator iter = method.code().listIterator(); + + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (CounterDecorate.VERBOSE > 2) { + System.out.println("Examining " + ce); + } + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + if (inst.opcodeClass() == Opcode.opcx_aupdate) { + iter.remove(); + iter.add(new Instruction(Opcode.opcx_getstatic, aufield)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); + iter.next(); + iter.add(new Instruction(Opcode.opcx_iadd)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_putstatic, aufield)); + iter.next(); + } + if (inst.opcodeClass() == Opcode.opcx_supdate) { + iter.remove(); + iter.add(new Instruction(Opcode.opcx_getstatic, sufield)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); + iter.next(); + iter.add(new Instruction(Opcode.opcx_iadd)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_putstatic, sufield)); + iter.next(); + } else if (inst.opcodeClass() == Opcode.opcx_rc) { + iter.remove(); + iter.add(new Instruction(Opcode.opcx_getstatic, rcfield)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_ldc, new Integer(1))); + iter.next(); + iter.add(new Instruction(Opcode.opcx_iadd)); + iter.next(); + iter.add(new Instruction(Opcode.opcx_putstatic, rcfield)); + iter.next(); + } + } + } + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/Makefile b/src/EDU/purdue/cs/bloat/benchmark/Makefile new file mode 100644 index 0000000..d866c6b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Makefile @@ -0,0 +1,129 @@ +# 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. +# +# 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 + + +CLASS = \ + Benchmark.class\ + Shade.class\ + Stats.class\ + BenchmarkSecurityManager.class\ + Nonstop.class\ + Times.class\ + CounterDecorate.class + +JNIH = EDU_purdue_cs_bloat_benchmark_Benchmark.h \ + EDU_purdue_cs_bloat_benchmark_Shade.h \ + EDU_purdue_cs_bloat_benchmark_Stats.h \ + EDU_purdue_cs_bloat_benchmark_Times.h \ + EDU_purdue_cs_bloat_benchmark_Nonstop.h + +OBJ = benchmark.o shade.o stats.o nonstop.o times.o + +LIB = libbenchmark_g.so libbenchmark.so libshade.so libstats.so libstats_g.so libnonstop.so libtimes.so libtimes_g.so + +.SUFFIXES: .java .class + +JAVA_HOME = /u/u83/pps/java +JAVAC = $(JAVA_HOME)/bin/javac +JAVAH = $(JAVA_HOME)/bin/javah +JFLAGS = -g +CLASSPATH = $(JAVA_HOME)/lib/classes.zip +CFLAGS = -K pic -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/solaris \ + -I/u/u83/pps/perfmon/include + +all: class $(LIB) + +clean: + rm -f *.class *.o $(JNIH) $(OBJ) $(LIB) + +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 $< + +libbenchmark.so: benchmark.o + cc benchmark.o -o libbenchmark.so /u/u83/pps/perfmon/lib/libperfmon.a -G + +libbenchmark_g.so: benchmark.o + cc benchmark.o -o libbenchmark_g.so /u/u83/pps/perfmon/lib/libperfmon.a -G + +libshade.so: shade.o + cc shade.o -o libshade.so -G + +libstats_g.so: stats.o + cc stats.o -o libstats_g.so -G + +libstats.so: stats.o + cc stats.o -o libstats.so -G + +libtimes.so: times.o + cc times.o -o libtimes.so -G + +libtimes_g.so: times.o + cc times.o -o libtimes_g.so -G + +libnonstop.so: nonstop.o + cc nonstop.o -o libnonstop.so -G + +EDU_purdue_cs_bloat_benchmark_Benchmark.h: Benchmark.java + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAH) -jni -classpath $$cpath \ + EDU.purdue.cs.bloat.benchmark.Benchmark + +EDU_purdue_cs_bloat_benchmark_Shade.h: Shade.java + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAH) -jni -classpath $$cpath \ + EDU.purdue.cs.bloat.benchmark.Shade + +EDU_purdue_cs_bloat_benchmark_Stats.h: Stats.java + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAH) -jni -classpath $$cpath \ + EDU.purdue.cs.bloat.benchmark.Stats + +EDU_purdue_cs_bloat_benchmark_Times.h: Times.java + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAH) -jni -classpath $$cpath \ + EDU.purdue.cs.bloat.benchmark.Times + +EDU_purdue_cs_bloat_benchmark_Nonstop.h: Nonstop.java + cpath=$(CLASSPATH):`(cd ../../../../..; pwd)`; \ + $(JAVAH) -jni -classpath $$cpath \ + EDU.purdue.cs.bloat.benchmark.Nonstop + +nonstop.o: nonstop.c EDU_purdue_cs_bloat_benchmark_Nonstop.h + +benchmark.o: benchmark.c EDU_purdue_cs_bloat_benchmark_Benchmark.h + +times.o: times.c EDU_purdue_cs_bloat_benchmark_Times.h + +shade.o: shade.c EDU_purdue_cs_bloat_benchmark_Shade.h + +stats.o: stats.c EDU_purdue_cs_bloat_benchmark_Stats.h + +.c.o: + cc -c $(CFLAGS) $< + diff --git a/src/EDU/purdue/cs/bloat/benchmark/Nonstop.java b/src/EDU/purdue/cs/bloat/benchmark/Nonstop.java new file mode 100644 index 0000000..dc9c891 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Nonstop.java @@ -0,0 +1,101 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +/** + * Runs a Java program multiple times without the Virtual Machine exiting. + * + * @see BenchmarkSecurityManager + */ +public class Nonstop { + static { + // Load native code from libbenchmark.so + System.loadLibrary("nonstop"); + } + + public static native void run(Class main, String[] args); + + public static void main(final String[] args) throws Exception { + int runs = 1; + int eat = 0; + + if (args.length <= 1) { + Nonstop.usage(); + } + + for (eat = 0; eat < args.length; eat++) { + if (args[eat].equals("-run")) { + if (++eat >= args.length) { + Nonstop.usage(); + } + + runs = Integer.parseInt(args[eat]); + + if (runs <= 0) { + Nonstop.usage(); + } + + } else { + // The main class + eat++; + break; + } + } + + /* Got all the args. */ + if (eat > args.length) { + Nonstop.usage(); + } + + final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); + System.setSecurityManager(sec); + + final String mainClassName = args[eat - 1]; + final String[] a = new String[args.length - eat]; + + System.err.println("Running " + mainClassName); + + for (int i = 0; i < runs; i++) { + try { + final Class mainClass = Class.forName(mainClassName); + + System.arraycopy(args, eat, a, 0, a.length); + + Benchmark.run(mainClass, a); + } catch (final SecurityException e) { + continue; + } catch (final Exception e) { + e.printStackTrace(System.err); + sec.allowExit = true; + System.exit(1); + } + } + + sec.allowExit = true; + } + + private static void usage() { + System.err.print("usage: java EDU.purdue.cs.bloat.Benchmark "); + System.err.println("options class args..."); + System.err.println("where options are one of:"); + System.exit(1); + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/Shade.java b/src/EDU/purdue/cs/bloat/benchmark/Shade.java new file mode 100644 index 0000000..e6d3816 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Shade.java @@ -0,0 +1,110 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +/** + * This class is used to execute the BLOAT benchmarks while the Shade + * performanace monitoring software is running. + */ +public class Shade { + static { + System.loadLibrary("shade"); + } + + public static native void run(Class main, String[] args, boolean quit); + + public static void main(final String[] args) throws Exception { + boolean quit = false; + int runs = 1; + int eat = 0; + + if (args.length <= 1) { + Shade.usage(); + } + + for (eat = 0; eat < args.length; eat++) { + if (args[eat].equals("-quit")) { + quit = true; + } else if (args[eat].equals("-run")) { + if (++eat >= args.length) { + Shade.usage(); + } + + runs = Integer.parseInt(args[eat]); + + if (runs <= 0) { + Shade.usage(); + } + } else { + // The main class + eat++; + break; + } + } + + /* Got all the args. */ + if (eat > args.length) { + Shade.usage(); + } + + // Install a secutiry manager in which we can control whether or + // not the virtual machine is allowed to exit. We want to be able + // to make multiple runs of the main class without the VM exiting. + final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); + System.setSecurityManager(sec); + + final String mainClassName = args[eat - 1]; + final String[] a = new String[args.length - eat]; + + System.err.println("Running " + mainClassName); + + for (int i = 0; i < runs; i++) { + try { + final Class mainClass = Class.forName(mainClassName); + + System.arraycopy(args, eat, a, 0, a.length); + + Shade.run(mainClass, a, quit); + + } catch (final SecurityException e) { + // An execution of the mainClass finished and the VM attempted + // to exit, thus causing a SecutiryException to be thrown by + // the BenchmarkSecurityManager. + continue; + + } catch (final Exception e) { + e.printStackTrace(System.err); + sec.allowExit = true; + System.exit(1); + } + } + + sec.allowExit = true; + } + + private static void usage() { + System.err.print("usage: java EDU.purdue.cs.bloat.Shade "); + System.err.println("options class args..."); + System.err.println("where options are one of:"); + System.err.println(" -run n time n runs of the program"); + System.exit(1); + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/Stats.java b/src/EDU/purdue/cs/bloat/benchmark/Stats.java new file mode 100644 index 0000000..7a1f470 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Stats.java @@ -0,0 +1,105 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +/** + * Stats is used to run a benchmark using an instrumented Java + * Virtual machine that counts the number of each kind of bytecodes executed. + * The counts are maintained in two C variables, instruction_count + * and redundant_count. + */ +public class Stats { + + static { + System.loadLibrary("stats"); + } + + public static native void run(Class main, String[] args); + + public static void main(final String[] args) throws Exception { + int runs = 1; + int eat = 0; + + if (args.length <= 1) { + Stats.usage(); + } + + for (eat = 0; eat < args.length; eat++) { + if (args[eat].equals("-run")) { + if (++eat >= args.length) { + Stats.usage(); + } + + runs = Integer.parseInt(args[eat]); + + if (runs <= 0) { + Stats.usage(); + } + + } else { + // The main class + eat++; + break; + } + } + + /* Got all the args. */ + if (eat > args.length) { + Stats.usage(); + } + + final BenchmarkSecurityManager sec = new BenchmarkSecurityManager(); + System.setSecurityManager(sec); + + final String mainClassName = args[eat - 1]; + final String[] a = new String[args.length - eat]; + + System.err.println("Running " + mainClassName); + + for (int i = 0; i < runs; i++) { + try { + final Class mainClass = Class.forName(mainClassName); + + System.arraycopy(args, eat, a, 0, a.length); + + Stats.run(mainClass, a); + + } catch (final SecurityException e) { + continue; + + } catch (final Exception e) { + e.printStackTrace(System.err); + sec.allowExit = true; + System.exit(1); + } + } + + sec.allowExit = true; + } + + private static void usage() { + System.err.print("usage: java EDU.purdue.cs.bloat.Stats "); + System.err.println("options class args..."); + System.err.println("where options are one of:"); + System.err.println(" -run n time n runs of the program"); + System.exit(1); + } +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/Times.java b/src/EDU/purdue/cs/bloat/benchmark/Times.java new file mode 100644 index 0000000..d232b8d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/Times.java @@ -0,0 +1,76 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.benchmark; + +/** + * This class allows Java to access the information obtained by the UNIX system + * call times. + */ +public class Times { + static { + // Load native code from libbenchmark.so + System.loadLibrary("times"); + } + + static float userTime; + + static float systemTime; + + /** + * Takes a "snapshot" of the system. Reads various items from the result of + * times. + * + * @return true if everything is successful + */ + public static native boolean snapshot(); + + /** + * Returns the user time used by this process in seconds. + */ + public static float userTime() { + return (Times.userTime); + } + + /** + * Returns the system time used by this process in seconds. + */ + public static float systemTime() { + return (Times.systemTime); + } + + /** + * Test program. + */ + public static void main(final String[] args) throws Exception { + System.out.println("Starting Test"); + + if (Times.snapshot() == false) { + System.err.println("Error during snapshot"); + System.exit(1); + } + + System.out.println("System time: " + Times.systemTime()); + System.out.println("User time: " + Times.userTime()); + + System.out.println("Ending Test"); + } + +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/benchmark.c b/src/EDU/purdue/cs/bloat/benchmark/benchmark.c new file mode 100644 index 0000000..251a622 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/benchmark.c @@ -0,0 +1,260 @@ +/* + * This C file provides the implementation of + * EDU.purdue.cs.bloat.Benchmark's native run method. It is loosely + * based on the "timeit" example found in: + * /u/u83/pps/perfmon/examples/ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "EDU_purdue_cs_bloat_benchmark_Benchmark.h" +#include "perfmon.h" + +#define MAIN_NAME "main" +#define MAIN_SIG "([Ljava/lang/String;)V" +#define FALSE 0 +#define TRUE (!FALSE) +#define PIC_SNAPSHOT_INTERVAL 5 +#define check_syscall(exp) \ + do { if ((exp) == -1) die_with_errno(#exp, __FILE__, __LINE__); } while(0); + +#ifndef U +#define U(a) a +#endif + + +/* Global locations for holding PIC values */ +static unsigned long long pic0_counter; /* Cumulative PIC0 */ +static unsigned long long pic1_counter; /* Cumulative PIC1 */ +static unsigned long pic0_interval; /* Change since last alarm */ +static unsigned long pic1_interval; + +/* Other interesting global variables */ +static int snapshotting = TRUE; +static int want_realtime = FALSE; +static int processor = 0; /* Processor we use */ +static int mode; /* Determines what we count */ + +/* Stuff for running method */ +static jmethodID method; /* Pointer to main method */ + +/* Error reporting function */ +static void +die_with_errno(const char* s, const char* file, int line) +{ + fprintf( stderr, "%s:%d:%s:%s\n", file, line, s, strerror(errno) ); + exit(1); +} + + +/* Functions for reading the PIC registers and keeping a running total + * in memory. Called before the Java program is run and when the + * alarm goes off (i.e. when a "snapshot is taken"). + */ +static void start_pic_snapshot() { + unsigned long long pic; + + pic = get_pic(); + pic0_interval -= extract_pic0( pic ); + pic1_interval -= extract_pic1( pic ); +} + +/* Computes the difference in the PIC between now and the last + * "snapshot" (i.e. the "interval"). Adds increments the overall + * counts by these values. Resets the interval. + */ +static void end_pic_snapshot() { + unsigned long long pic; + + pic = get_pic(); + + pic0_interval += extract_pic0( pic ); + pic1_interval += extract_pic1( pic ); + + pic0_counter += (unsigned long long)pic0_interval; + pic1_counter += (unsigned long long)pic1_interval; + + pic0_interval = pic1_interval = 0; +} + +/* Function to catch interrupts and update the PIC values. This is + * called whenever the alarm "goes off". + */ +static void alarm_handler( int U(sig), + siginfo_t* U(info), + ucontext_t* U(ctxt) ) +{ + if ( snapshotting ) { + end_pic_snapshot(); + start_pic_snapshot(); + alarm(PIC_SNAPSHOT_INTERVAL); + } +} + +/* + * Performs initialization necessary for running a Java program + * multiple times and monitoring what goes on using perfmon. For + * instance, we obtain a pointer to the main method, set up the alarm + * stuff, and initialize perfmon. + * + * Runs a Java program (a main method) with the perfmon counters + * turned on. We have to be careful to make sure that we correctly + * account for counters that may overflow. Thus, we set an alarm to + * go off every five seconds and record the counters. The code that + * handles all of the alarm stuff was contributed by Kevin Corry. + * + * Parameters: + * env JNI Environment + * clazz Reference to EDU.purdue.cs.bloat.benchmark.Benchmark + * main Class containing main method + * args Arguments to main method + */ +JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_init( + JNIEnv *env, jclass clazz, jclass main) +{ + unsigned long long pcr; + int fd; + struct sigaction act; + + (*env)->ExceptionClear(env); + + method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); + + if ((*env)->ExceptionOccurred(env) != NULL) { + fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); + return; + } + + /* Determine which things we want to count */ + if (mode == 0) { + /* Count load interlock induced stalls and instructions. */ + pcr = PCR_USER_TRACE | PCR_S0_STALL_LOAD | PCR_S1_INSTR_CNT; + + } else if (mode == 1) { + /* Count data cache hit rate. */ + pcr = PCR_USER_TRACE | PCR_S0_DC_READ | PCR_S1_DC_READ_HIT; + + } else if (mode == 2) { + /* Count icache miss induced stalls and cycles. */ + pcr = PCR_USER_TRACE | PCR_S0_STALL_IC_MISS | PCR_S1_CYCLE_CNT; + + } else if (mode == 3) { + /* Count instructions and cycles. */ + pcr = PCR_USER_TRACE | PCR_S0_INSTR_CNT | PCR_S1_CYCLE_CNT; + + } else { + fprintf(stderr, "invalid mode: %d, must be [012]\n", mode); + exit(1); + } + + /* Set up perfmon. Bind this thread to a processor so that it can't + get away from us. Flush the cache for the heck of it. */ + check_syscall(processor_bind(P_PID, P_MYID, processor, 0)); + check_syscall(fd = open( "/dev/perfmon", O_RDONLY)); + check_syscall(ioctl(fd, PERFMON_FLUSH_CACHE )); + check_syscall(ioctl(fd, PERFMON_SETPCR, &pcr)); + check_syscall(close(fd)); + + /* Set up the periodic interrupts to make measurements. This way + we don't have to worry about the PIC counters wrapping around. */ + act.sa_handler = 0; + act.sa_sigaction = (void(*)(int,siginfo_t*,void*))alarm_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + check_syscall(sigaction(SIGALRM, &act, 0)); +} + +/* + * Actually runs the program and takes measurements before and after. + * Prints the results out. + */ +JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_run( + JNIEnv *env, jclass clazz, jclass main, jobjectArray args) { + pid_t pid; + + hrtime_t starttime; + hrtime_t endtime; + unsigned long long starttick; + unsigned long long endtick; + + /* Initialize counters and intervals */ + snapshotting = TRUE; + pic0_counter = pic1_counter = 0LL; + pic0_interval = pic1_interval = 0; + + /* Print this guy. I'm not too sure why. */ + fprintf(stderr, "0x%x\n", &gethrtime); + + /* Take an initial reading of the PICs */ + fprintf(stderr, "reset\n"); + cpu_sync(); + clr_pic(); + start_pic_snapshot(); + + starttick = get_tick(); /* Get number of cycles since power-on */ + starttime = gethrtime(); /* Get the (high-resolution) time */ + + alarm(PIC_SNAPSHOT_INTERVAL); /* Set the alarm */ + check_syscall(processor_bind(P_PID, P_MYID, processor, 0)); + + /* Run the Java program (benchmark). */ + (*env)->CallStaticVoidMethod(env, main, method, args); + + /* Get whatever data from the counters and report it. */ + cpu_sync(); + snapshotting = FALSE; /* No more snapshooting */ + end_pic_snapshot(); + alarm(0); /* Turn off alarm */ + + endtime = gethrtime(); /* Get time */ + endtick = get_tick(); /* Get number of cycles since power-on */ + + fprintf(stderr, "wall time %llu ns\n", endtime-starttime); + fprintf(stderr, "ticks %llu\n", endtick-starttick); + + if (mode == 0) { + /* Count load stalls and instructions. */ + fprintf(stderr, "load stalls %llu\n", pic0_counter); + fprintf(stderr, "insts %llu\n", pic1_counter); + } + else if (mode == 1) { + /* Count data cache hit rate. */ + fprintf(stderr, "D-cache reads %llu\n", pic0_counter); + fprintf(stderr, "D-cache hits %llu\n", pic1_counter); + } + else if (mode == 2) { + /* Count I-cache miss stalls and cycles. */ + fprintf(stderr, "I-miss stalls %llu\n", pic0_counter); + fprintf(stderr, "cycles %llu\n", pic1_counter); + } + else if (mode == 3) { + /* Count instructions and cycles. */ + fprintf(stderr, "insts %llu\n", pic0_counter); + fprintf(stderr, "cycles %llu\n", pic1_counter); + } + + + fflush(stderr); +} + +/* Set the mode. The mode tells us which things to count. + */ +JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Benchmark_setMode( + JNIEnv *env, jclass clazz, jint m) +{ + mode = m; +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/package.html b/src/EDU/purdue/cs/bloat/benchmark/package.html new file mode 100644 index 0000000..08cba49 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/package.html @@ -0,0 +1,10 @@ + + + +

Contains classes that are used to run benchmarks that measure the +performance of BLOATed code. These classes allow us to do things like +run a program several times in the same virtual machine +invocation.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/benchmark/shade.c b/src/EDU/purdue/cs/bloat/benchmark/shade.c new file mode 100644 index 0000000..f2e24df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/shade.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include "EDU_purdue_cs_bloat_benchmark_Shade.h" + +#define MAIN_NAME "main" +#define MAIN_SIG "([Ljava/lang/String;)V" + +JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Shade_run( + JNIEnv *env, jclass clazz, jclass main, jobjectArray args, jboolean quit) +{ + jmethodID method; + + (*env)->ExceptionClear(env); + + method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); + + if ((*env)->ExceptionOccurred(env) != NULL) { + fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); + return; + } + + system("sh ./run_pre"); + + if (quit) { + fprintf(stderr, "0x%x\n", &gethrtime); + fflush(stderr); + return; + } + + gethrtime(); + + (*env)->CallStaticVoidMethod(env, main, method, args); + + gethrtime(); +} diff --git a/src/EDU/purdue/cs/bloat/benchmark/stats.c b/src/EDU/purdue/cs/bloat/benchmark/stats.c new file mode 100644 index 0000000..fe0619c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/benchmark/stats.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include "EDU_purdue_cs_bloat_benchmark_Stats.h" + +extern int instruction_count[256][256]; +extern int redundant_count[256]; + +#define MAIN_NAME "main" +#define MAIN_SIG "([Ljava/lang/String;)V" + +JNIEXPORT void JNICALL Java_EDU_purdue_cs_bloat_benchmark_Stats_run( + JNIEnv *env, jclass clazz, jclass main, jobjectArray args) { + jmethodID method; + + (*env)->ExceptionClear(env); + + method = (*env)->GetStaticMethodID(env, main, MAIN_NAME, MAIN_SIG); + + if ((*env)->ExceptionOccurred(env) != NULL) { + fprintf(stderr, "Method not found: %s%s\n", MAIN_NAME, MAIN_SIG); + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); + return; + } + + system("sh ./run_pre"); + + memset(instruction_count, 0, sizeof(instruction_count)); + memset(redundant_count, 0, sizeof(redundant_count)); + + (*env)->CallStaticVoidMethod(env, main, method, args); +} diff --git a/src/EDU/purdue/cs/bloat/cfg/.cvsignore b/src/EDU/purdue/cs/bloat/cfg/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file 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..eb16645 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/Block.java @@ -0,0 +1,371 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 mutiple 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 + + Label 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 + + StackOptimizer stackOptimizer; // Stack Optimizer + + /** + * Constructor. + * + * @param label + * The block's label. The label may be thought of as the line of + * code at which the block begins. + * @param graph + * The CFG containing the block. + */ + Block(final Label label, final FlowGraph graph) { + this.label = label; + this.graph = graph; + this.tree = null; + this.header = null; + this.blockType = Block.NON_HEADER; + + label.setStartsBlock(true); + + domParent = null; + pdomParent = null; + + domChildren = new HashSet(); + pdomChildren = new HashSet(); + + domFrontier = new HashSet(); + pdomFrontier = new HashSet(); + + stackOptimizer = new StackOptimizer(this); // make StackOptimizer + // object + + } + + /** + * Returns the stack optimizer for this block. + * + * @return The stack optimizer. + */ + public StackOptimizer 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(final 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 Label label() { + return label; + } + + /** + * Visits the expression tree contained in this block. + */ + public void visitChildren(final TreeVisitor visitor) { + if (tree != null) { + tree.visit(visitor); + } + } + + public void visit(final 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(final 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(final 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(final 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(final 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(final 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(final 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..1383912 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/DominanceFrontier.java @@ -0,0 +1,158 @@ +/** + * 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. + * + * 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 + */ + +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(final FlowGraph graph, boolean reverse) { + if (!reverse) { + DominanceFrontier.calcFrontier(graph.source(), graph, reverse); + } else { + DominanceFrontier.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(final Block block, + final 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. + final 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()) { + final Block child = (Block) children.next(); + + final LinkedList df = DominanceFrontier.calcFrontier(child, graph, + reverse); + + final Iterator e = df.iterator(); + + while (e.hasNext()) { + final 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; + } + } + } + } + + final 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()) { + final 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; + } + } + } + + final 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..79a9961 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/DominatorTree.java @@ -0,0 +1,387 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.util.*; + +import EDU.purdue.cs.bloat.util.*; + +/** + * 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(final FlowGraph graph, boolean reverse) { + final int size = graph.size(); // The number of vertices in the cfg + + final Map snkPreds = new HashMap(); // The predacessor vertices from the + // sink + + // Determine the predacessors of the cfg's sink node + DominatorTree.insertEdgesToSink(graph, snkPreds, reverse); + + // Get the index of the root + final 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. + final BitSet[] dom = new BitSet[size]; + + // A bit vector of all 1's + final 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++) { + final 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 + final 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()) { + final Block block = (Block) blocks.next(); + + final 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; + } + + final BitSet oldSet = dom[i]; + final BitSet blockDoms = new BitSet(size); + blockDoms.or(oldSet); + + // print(graph, reverse, "old set", i, blockDoms); + + // 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()) { + final Block pred = (Block) e.next(); + + final 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()) { + final Block pred = (Block) e.next(); + + final int j = graph.preOrderIndex(pred); + Assert.isTrue(j >= 0, "Unreachable block " + pred); + + blockDoms.and(dom[j]); + } + } + + // Include yourself in your dominators?! + blockDoms.set(i); + + // print(graph, reverse, "intersecting " + preds, i, blockDoms); + + // 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()) { + final 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()) { + final Block block = (Block) blocks.next(); + + final 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 + final BitSet blockDoms = dom[i]; + + // print(graph, reverse, "dom set", i, blockDoms); + + final BitSet idom = new BitSet(size); + idom.or(blockDoms); + idom.clear(i); + + for (int j = 0; j < size; j++) { + if ((i != j) && blockDoms.get(j)) { + final BitSet domDomBlocks = dom[j]; + + // idom = idom - (domDomBlocks - {j}) + final BitSet b = new BitSet(size); + b.or(domDomBlocks); + b.xor(ALL); + b.set(j); + idom.and(b); + + // print(graph, reverse, + // "removing dom(" + graph.preOrder().get(j) +")", + // i, idom); + } + } + + Block parent = null; + + // A block should only have one immediate dominator. + for (int j = 0; j < size; j++) { + if (idom.get(j)) { + final 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 (DominatorTree.DEBUG) { + System.out.println(parent + " dominates " + block); + } + + block.setDomParent(parent); + + } else { + if (DominatorTree.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(final FlowGraph graph, + final Map preds, final boolean reverse) { + final BitSet visited = new BitSet(); // see insertEdgesToSinkDFS + final BitSet returned = new BitSet(); + + visited.set(graph.preOrderIndex(graph.source())); + + DominatorTree.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(final FlowGraph graph, + final Block block, final BitSet visited, final BitSet returned, + final Map preds, boolean reverse) { + boolean leaf = true; // Is a vertex a leaf node? + + // Get the successors of block + final Iterator e = graph.succs(block).iterator(); + + while (e.hasNext()) { + final Block succ = (Block) e.next(); + + // Determine index of succ vertex in a pre-order traversal + final 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); + DominatorTree.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 HashSet to + // store them and register it in the pred Map. + if (p == null) { + p = new HashSet(); + 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 HashSet(); + 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..c0bf11f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/FlowGraph.java @@ -0,0 +1,3491 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.codegen.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.trans.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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; + + MethodEditor method; // The method that we create a CFG for. + + 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; + + private void db(final String s) { + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + System.out.println(s); + } + } + + /** + * Constructor. + * + * @param method + * The method to create the CFG for. + */ + public FlowGraph(final MethodEditor method) { + this.method = method; + + subroutines = new HashMap(); + catchBlocks = new ArrayList(method.tryCatches().size()); + handlers = new HashMap(method.tryCatches().size() * 2 + 1); + trace = new LinkedList(); + + srcBlock = newBlock(); + iniBlock = newBlock(); + snkBlock = newBlock(); + + trace.add(iniBlock); + + // If this method is empty(!) just make some default cfg edges + if (method.codeLength() == 0) { + addEdge(srcBlock, iniBlock); + addEdge(iniBlock, snkBlock); + addEdge(srcBlock, snkBlock); + + buildSpecialTrees(null, null); + + return; + } + + final Map labelPos = new HashMap(); + buildBlocks(labelPos); + 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.codeLength() == 0) { + computeDominators(); + buildLoopTree(); + return; + } + + // Determine which vertices dominate which vertices, update the blocks + // in the cfg appropriately + computeDominators(); + + if (FlowGraph.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 (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting phi blocks (Begin)"); + this.print(System.out); + db("------ After splitting phi blocks (End)"); + } + + removeUnreachable(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 1 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 1 (End)"); + } + + splitIrreducibleLoops(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting irreduciable loops (Begin)"); + this.print(System.out); + db("------ After splitting irreducible loops (End)"); + } + + removeUnreachable(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 2 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 2 (End)"); + } + + splitReducibleLoops(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After splitting reducible loops (Begin)"); + this.print(System.out); + db("------ After splitting reducible loops (End)"); + } + + removeUnreachable(); + + if (FlowGraph.DEBUG || FlowGraph.DB_GRAPHS) { + db("------ After removing unreachable 3 (Begin)"); + this.print(System.out); + db("------ After removing unreachable 3 (End)"); + } + + buildLoopTree(); + + peelLoops(FlowGraph.PEEL_LOOPS_LEVEL); + + 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 + */ + private 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() { + final 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()) { + final Block block = (Block) iter.next(); + final 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()) { + final LoopNode loop = (LoopNode) iter.next(); + + // The level of the node is max(level(succs)) + 1. + int level = 0; + + final Iterator succs = loopTree.succs(loop).iterator(); + + while (succs.hasNext()) { + final 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()) { + final LoopNode loop = (LoopNode) iter.next(); + + // The depth of the node is depth(pred) + 1. + final Iterator preds = loopTree.preds(loop).iterator(); + + if (preds.hasNext()) { + final 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. + * + * @param labelPos + * A mapping between the Labels in the code that start a basic + * block and their offset in the code (an Integer). + */ + private void buildBlocks(final Map labelPos) { + db(" Building blocks"); + + // Get the Labels and Instructions of this method + ListIterator iter = method.code().listIterator(); + + // Go through the code, find each Label that starts a block, create + // a Block for that Label, and add it to the trace. + while (iter.hasNext()) { + final Object obj = iter.next(); + + if (obj instanceof Label) { + final Label label = (Label) obj; + + if (label.startsBlock()) { + trace.add(newBlock(label)); + } + } + } + + Instruction lastInst = null; + + Block currBlock = iniBlock; + Block firstBlock = null; + + int i = 0; + iter = method.code().listIterator(); + + while (iter.hasNext()) { + final Object curr = iter.next(); + + if (curr instanceof Label) { + final Label label = (Label) curr; + + if (label.startsBlock()) { + labelPos.put(label, new Integer(i)); + + final Block nextBlock = (Block) getNode(label); + + // 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 ((lastInst != null) && lastInst.isJsr()) { + final Block target = (Block) getNode(lastInst.operand()); + final Subroutine sub = (Subroutine) subroutines + .get(target); + sub.addPath(currBlock, nextBlock); + } + + currBlock = nextBlock; // Go on to next block + + if (firstBlock == null) { + firstBlock = currBlock; + } + } + + } else if (curr instanceof Instruction) { + final Instruction currInst = (Instruction) curr; + + lastInst = currInst; + + // Call setsubEntry to maintain a mapping between the entry + // block of a Subroutine and the Subroutine itself + if (currInst.isJsr()) { + final Label label = (Label) currInst.operand(); + final Block target = (Block) getNode(label); + + if (!subroutines.containsKey(target)) { + final Subroutine sub = new Subroutine(this); + setSubEntry(sub, target); + } + } + } else { + throw new IllegalArgumentException(); + } + + i++; // Go to next instruction + } + + // Start the tedious process of building the expression trees for + // the basic blocks + buildTrees(firstBlock, labelPos); + } + + /** + * 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. + * @param labelPos + * A mapping between Labels and their instruction number (offset + * into the code). + */ + private void buildTrees(final Block firstBlock, final Map labelPos) { + 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). + final HashMap catchBodies = new HashMap( + method.tryCatches().size() * 2 + 1); + + final Iterator tryCatches = method.tryCatches().iterator(); + + while (tryCatches.hasNext()) { + final TryCatch tc = (TryCatch) 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 handler. + final Block catchBlock = newBlock(); + + // This block will hold the instructions in the catch body. + final Block catchBody = (Block) getNode(tc.handler()); + + catchBodies.put(catchBlock, catchBody); + + // Make sure we include the new block in any protected area + // containing the catch body. + Integer pos = (Integer) labelPos.get(tc.handler()); + labelPos.put(catchBlock.label(), pos); + + addEdge(catchBlock, catchBody); + trace.add(trace.indexOf(catchBody), catchBlock); + + Type type = tc.type(); + + if (type == null) { + type = Type.NULL; + } + + catchBlocks.add(catchBlock); + + // Save the exception to the stack. + final StackExpr lhs = new StackExpr(0, Type.THROWABLE); + final CatchExpr rhs = new CatchExpr(type, Type.THROWABLE); + final StoreExpr store = new StoreExpr(lhs, rhs, Type.THROWABLE); + + // Build the tree for the exception handler target block. + final Tree tree = new Tree(catchBlock, new OperandStack()); + catchBlock.setTree(tree); + tree.addStmt(new ExprStmt(store)); + tree.addStmt(new GotoStmt(catchBody)); + + // Create the Handler. + final Integer start = (Integer) labelPos.get(tc.start()); + final Integer end = (Integer) labelPos.get(tc.end()); + + final Handler handler = new Handler(catchBlock, type); + handlers.put(catchBlock, handler); + + final Iterator blocks = nodes().iterator(); + + // 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. + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + pos = (Integer) labelPos.get(block.label()); + + if (pos == null) { + // This is one of the special blocks such as the source, + // sink, and init block. + continue; + } + + if (start.intValue() <= pos.intValue()) { + if ((end == null) || (pos.intValue() < end.intValue())) { + handler.protectedBlocks().add(block); + } + } + } + } + + addEdge(srcBlock, iniBlock); + addEdge(srcBlock, snkBlock); + addEdge(iniBlock, firstBlock); + + buildSpecialTrees(catchBodies, labelPos); + + // Build the trees for the blocks reachable from the firstBlock. + buildTreeForBlock(firstBlock, iniBlock.tree().stack(), null, labelPos, + 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(); } + * + */ + private void insertConditionalStores() { + db(" Inserting conditional stores"); + + final Iterator blocks = new ImmutableIterator(nodes()); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Stmt last = block.tree().lastStmt(); + + if (last instanceof IfCmpStmt) { + final 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().isReference() + && !right.type().isReference()) { + + // 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)) { + final LocalVariable v = method + .newLocal(left.type()); + final LocalExpr tmp = new LocalExpr(v.index(), left + .type()); + final Expr copy = (Expr) left.clone(); + copy.setDef(null); + left.replaceWith(new StoreExpr(tmp, copy, left + .type())); + left = tmp; + } + + if (!(right instanceof LeafExpr)) { + final LocalVariable v = method.newLocal(right + .type()); + final LocalExpr tmp = new LocalExpr(v.index(), + right.type()); + final 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) { + final LocalExpr tmp = (LocalExpr) left.clone(); + tmp.setDef(null); + final Expr copy = (Expr) right.clone(); + copy.setDef(null); + final Stmt insert = new ExprStmt(new StoreExpr(tmp, + copy, left.type())); + + target.tree().prependStmt(insert); + + } else if (right instanceof LocalExpr) { + final LocalExpr tmp = (LocalExpr) right.clone(); + tmp.setDef(null); + final Expr copy = (Expr) left.clone(); + copy.setDef(null); + final 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) { + final 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().isReference()) { + // 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)) { + final LocalVariable v = method + .newLocal(left.type()); + final LocalExpr tmp = new LocalExpr(v.index(), left + .type()); + final 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; + + final Type type = left.type(); + + if (left.type().isIntegral()) { + value = new Integer(0); + + } else { + Assert.isTrue(left.type().isReference()); + } + + if (left instanceof LocalExpr) { + // Prepend the target block with an assignment from + // the + // value of the right hand side to the left + // expression. + + final LocalExpr copy = (LocalExpr) left.clone(); + copy.setDef(null); + final 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) { + final SwitchStmt stmt = (SwitchStmt) last; + + Expr index = stmt.index(); + + if (!(index instanceof LeafExpr)) { + // Replace index with a store into a new local variable + + final LocalVariable v = method.newLocal(index.type()); + final LocalExpr tmp = new LocalExpr(v.index(), index.type()); + final Expr copy = (Expr) index.clone(); + copy.setDef(null); + index.replaceWith(new StoreExpr(tmp, copy, index.type())); + index = tmp; + } + + if (index instanceof LocalExpr) { + final Block[] targets = stmt.targets(); + final 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; + // } + // + final HashSet seen = new HashSet(); + + // Targets that are branched to more than once + final HashSet duplicate = new HashSet(); + + 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++) { + final 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. + final LocalExpr copy = (LocalExpr) index.clone(); + copy.setDef(null); + final 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. + */ + private void insertProtectedRegionStores() { + db(" Inserting protected region stores"); + + final HashSet tryPreds = new HashSet(); + + final 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()) { + final Block block = (Block) blocks.next(); + + final Handler handler = (Handler) handlers.get(block); + + if (handler != null) { + final HashSet p = new HashSet(); + + final Iterator prots = handler.protectedBlocks().iterator(); + + while (prots.hasNext()) { + final 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()); + } + + /** + * Insters 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(final Block block, final 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(final LocalExpr expr) { + if (expr.isDef()) { + final int index = expr.index(); + + if (expr.type().isWide()) { + 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++) { + final 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(final Expr expr) { + StackExpr var = tree.newStack(expr.type()); + var.setValueNumber(expr.valueNumber()); + + final Node p = expr.parent(); + expr.setParent(null); + p.visit(new ReplaceVisitor(expr, var)); + + var = (StackExpr) var.clone(); + var.setDef(null); + final StoreExpr store = new StoreExpr(var, expr, + expr.type()); + store.setValueNumber(expr.valueNumber()); + + final Stmt storeStmt = new ExprStmt(store); + storeStmt.setValueNumber(expr.valueNumber()); + + tree.addStmtBeforeJump(storeStmt); + } + + public void visitStackExpr(final StackExpr expr) { + } + }); + + // Add assignment statements (StoreExpr) that store a copy + // of expr (a defining instance of LocalExpr) into itself. + + final LocalExpr copy1 = (LocalExpr) expr.clone(); + final LocalExpr copy2 = (LocalExpr) expr.clone(); + copy1.setDef(null); + copy2.setDef(null); + + final StoreExpr store = new StoreExpr(copy1, copy2, expr + .type()); + + tree.addStmtBeforeJump(new ExprStmt(store)); + } + } + } + + final 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()) { + final 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. + */ + private void saveLabels() { + // Make sure any labels in the removed blocks are saved. + boolean save = false; + + final Iterator iter = method.code().listIterator(); + + while (iter.hasNext()) { + final Object obj = iter.next(); + + if (obj instanceof Label) { + final Label label = (Label) obj; + + if (label.startsBlock()) { + if (getNode(label) == null) { + save = true; + } else { + save = false; + } + } + + if (save) { + label.setStartsBlock(false); + iniBlock.tree().addStmt(new LabelStmt(label)); + } + } + } + } + + /** + * Removes a subroutine from this method. + * + * @param sub + * The subroutine to remove. + */ + public void removeSub(final 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(final GraphNode src, final GraphNode dst) { + if (FlowGraph.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(final GraphNode v, final GraphNode w) { + final Block src = (Block) v; + final Block dst = (Block) w; + + if (FlowGraph.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(final PhiJoinStmt stmt) { + final 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(final Stmt stmt) { + } + }); + } + + /** + * Returns a new Block with the next available Label. + */ + public Block newBlock() { + return newBlock(method.newLabel()); + } + + /** + * 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(final Label label) { + final Block block = new Block(label, this); + addNode(label, block); + + if (FlowGraph.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 + */ + private 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"); + + final List blocks = preOrder(); + + // A block's predacessors that do not occur along back edges + final Set[] nonBackPreds = new Set[blocks.size()]; + + // A block's predacessors that DO occur along back edges + final Set[] backPreds = new Set[blocks.size()]; + + ListIterator iter = blocks.listIterator(); + + while (iter.hasNext()) { + final Block w = (Block) iter.next(); + final int wn = preOrderIndex(w); + + final Set nonBack = new HashSet(); + nonBackPreds[wn] = nonBack; + + final Set back = new HashSet(); + backPreds[wn] = back; + + w.setHeader(srcBlock); + w.setBlockType(Block.NON_HEADER); + + final Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + final 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); + + final UnionFind uf = new UnionFind(blocks.size()); + + iter = blocks.listIterator(blocks.size()); + + while (iter.hasPrevious()) { + final Block w = (Block) iter.previous(); + final int wn = preOrderIndex(w); + + final Set nonBack = nonBackPreds[wn]; + final Set back = backPreds[wn]; + + final Set body = new HashSet(); // The body of a loop + + final Iterator preds = back.iterator(); + + // For each loop header, follow the back edges to construct the + // body of the loop + while (preds.hasNext()) { + final Block v = (Block) preds.next(); + + if (v != w) { + final int vn = preOrderIndex(v); + final 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); + + final LinkedList worklist = new LinkedList(body); + + while (!worklist.isEmpty()) { + final Block x = (Block) worklist.removeFirst(); + final int xn = preOrderIndex(x); + + final Iterator e = nonBackPreds[xn].iterator(); + + while (e.hasNext()) { + final Block y = (Block) e.next(); // a block in the loop + final int yn = preOrderIndex(y); + final 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); + } + } + } + } + + final Iterator e = body.iterator(); + + // Merge all the blocks in the loop into the UnionFind set + while (e.hasNext()) { + final Block x = (Block) e.next(); + final 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()) { + final Subroutine sub = (Subroutine) e.next(); + + final Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + final 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()) { + final Block catchBlock = (Block) e.next(); + + if (catchBlock.blockType() != Block.NON_HEADER) { + catchBlock.setBlockType(Block.IRREDUCIBLE); + } + + final 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. + */ + private void splitIrreducibleLoops() { + db(" Splitting irreducible loops"); + + final 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()) { + final Block w = (Block) iter.next(); + + boolean hasReducibleBackIn = false; + final Set otherIn = new HashSet(); + + final Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + final 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)) { + final Iterator e = otherIn.iterator(); + + while (e.hasNext()) { + final Block v = (Block) e.next(); + removeEdges.add(new Block[] { v, w }); + } + } + } + + // Split the irreducible back edges. + iter = removeEdges.iterator(); + + while (iter.hasNext()) { + final 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. + */ + private void splitReducibleLoops() { + db(" Splitting reducible loops"); + + final Map reducibleBackIn = new HashMap(); + + final Stack stack = new Stack(); + + final Iterator iter = nodes().iterator(); + + while (iter.hasNext()) { + final Block w = (Block) iter.next(); + + final Set edges = new HashSet(); // reducible back edges + + final Iterator preds = preds(w).iterator(); + + while (preds.hasNext()) { + final 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()) { + final Block w = (Block) stack.pop(); + final 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()) { + final Block v = (Block) preds.next(); + final 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. + final Block newBlock = newBlock(); + + trace.add(trace.indexOf(w), newBlock); + + final Tree tree = new Tree(newBlock, min.tree().stack()); + newBlock.setTree(tree); + + tree.addInstruction(new Instruction(Opcode.opcx_goto, w.label())); + + // If the header is a protected block, the new block must be + // also since code can be moved from the header up. + final JumpStmt newJump = (JumpStmt) tree.lastStmt(); + + final Iterator e = handlers.values().iterator(); + + while (e.hasNext()) { + final 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()) { + final 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). + */ + private void peelLoops(final int level) { + if (FlowGraph.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 HashSet(); + + visit(new TreeVisitor() { + public void visitNode(final Node node) { + if (!hoistable.contains(node.block())) { + node.visitChildren(this); + } + } + + public void visitCastExpr(final CastExpr expr) { + if (expr.castType().isReference()) { + if (expr.expr() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + } + + visitNode(expr); + } + + public void visitArithExpr(final ArithExpr expr) { + if ((expr.operation() == ArithExpr.DIV) + || (expr.operation() == ArithExpr.REM)) { + if (expr.type().isIntegral() + && (expr.left() instanceof LeafExpr) + && (expr.right() instanceof LeafExpr)) { + hoistable.add(expr.block()); + } + } + + visitNode(expr); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + if (expr.array() instanceof LeafExpr) { + hoistable.add(expr.block()); + } + + visitNode(expr); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + if ((expr.array() instanceof LeafExpr) + && (expr.index() instanceof LeafExpr)) { + hoistable.add(expr.block()); + } + + visitNode(expr); + } + + public void visitFieldExpr(final 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 + final List peel = new ArrayList(loopTree.size()); + + // The header blocks of loops to be peeled + final List headers = new ArrayList(loopTree.size()); + + // The outer loop of the loops to be peeled (i.e. parent in the + // loop tree) + final List outer = new ArrayList(loopTree.size()); + + // All the loops in a tree in post-order + final List loops = new ArrayList(loopTree.postOrder()); + + for (int i = 0; i < loops.size(); i++) { + final 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; + + final Iterator e = loopTree.preds(loop).iterator(); + Assert.isTrue(e.hasNext()); + + outerLoop = (LoopNode) e.next(); + Assert.isTrue(!e.hasNext()); + + final int outerIndex = loops.indexOf(outerLoop); + Assert.isTrue(outerIndex != -1); + + outer.add(new Integer(outerIndex)); + } + } + + // The level of each loop to be peeled + final 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++) { + final 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 + final Integer loopIndex = (Integer) peel.get(i); + final Integer outerLoopIndex = (Integer) outer.get(i); + final Block header = (Block) headers.get(i); + + final Collection loop = (Collection) loops + .get(loopIndex.intValue()); + final 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 (FlowGraph.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 != FlowGraph.PEEL_NO_LOOPS) { + if ((level == FlowGraph.PEEL_ALL_LOOPS) + || (level >= levels[loopIndex.intValue()])) { + final Iterator e = loop.iterator(); + + while (e.hasNext()) { + final 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; + + final Iterator succs = succs(header).iterator(); + + while (succs.hasNext()) { + final 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 + final Set copySet = new HashSet(); + + if (canPeel) { + // Find the blocks which have exits outside the loop. + final Set exits = new HashSet(); + + // 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()) { + final Block block = (Block) e.next(); + + final Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + final 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; + } + } + } + + final ArrayList stack = new ArrayList(exits); + + e = exits.iterator(); + + // Add all "exit" blocks to the copy of the loop + while (e.hasNext()) { + final 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()) { + final Block block = (Block) stack.remove(stack.size() - 1); + + final Iterator preds = preds(block).iterator(); + + while (preds.hasNext()) { + final 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 + final Map copies = new HashMap(); + + 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()) { + final Block block = (Block) e.next(); + + // Jeez, are we dealing with a finally block? + if (FlowGraph.DEBUG) { + final Stmt jump = block.tree().lastStmt(); + + if (jump instanceof JsrStmt) { + final 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); + copies.put(block, copy); + } + + // Add the copy to the list of hositable blocks + if (hoistable.contains(block)) { + hoistable.add(copy); + } + } + } + + if (FlowGraph.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()) { + final Block pred = (Block) e.next(); + + if (!header.dominates(pred)) { + final 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. + final List copyTrace = new ResizeableArrayList(copies.size()); + + e = trace.iterator(); + + while (e.hasNext()) { + final Block block = (Block) e.next(); + final 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 + final List addEdges = new LinkedList(); + + // Edges to remove from the control flow graph + final 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()) { + final Map.Entry pair = (Map.Entry) e.next(); + + final Block block = (Block) pair.getKey(); + final Block copy = (Block) pair.getValue(); + + final Iterator h = handlers.values().iterator(); + + // The copy of the a protected block is also protected + while (h.hasNext()) { + final Handler handler = (Handler) h.next(); + + if (handler.protectedBlocks().contains(block)) { + handler.protectedBlocks().add(copy); + } + } + + final 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()) { + final Block succ = (Block) succs.next(); + final 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()) { + final Map.Entry pair = (Map.Entry) e.next(); + + final Block block = (Block) pair.getKey(); + final Block copy = (Block) pair.getValue(); + + final Iterator preds = preds(block).iterator(); + + while (preds.hasNext()) { + final 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()) { + final Block[] edge = (Block[]) e.next(); + addEdge(edge[0], edge[1]); + } + + e = removeEdges.iterator(); + + // Remove edges into the original (non-copied) loop + while (e.hasNext()) { + final Block[] edge = (Block[]) e.next(); + final Block v = edge[0]; + final 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 (FlowGraph.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(final Block block) { + final Block copy = newBlock(); + + // Copy the stack from the end of the old block. + // But don't change it when instructions are added. + + final Tree tree = new Tree(copy, block.tree().stack()); + copy.setTree(tree); + + // Fill the tree. + final Iterator stmts = block.tree().stmts().iterator(); + + while (stmts.hasNext()) { + final 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(final Label 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(final Subroutine sub, final 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(final PrintStream out) { + print(new PrintWriter(out, true)); + } + + /** + * Prints the graph. + * + * @param out + * The writer to which to print. + */ + public void print(final PrintWriter out) { + final String dateString = java.text.DateFormat.getDateInstance() + .format(new Date()); + out.println("Print " + ++file + " at " + dateString + " " + + method.type() + " " + method.name() + ":"); + + visit(new PrintVisitor(out)); + + if (FlowGraph.PRINT_GRAPH) { + printGraph(); + } + } + + int next = 1; + + public void printGraph() { + try { + final PrintStream out = new PrintStream(new FileOutputStream(method + .name() + + "." + next++ + ".dot")); + printGraph(out); + + } catch (final IOException e) { + } + + } + + public void print() { + try { + final PrintStream out = new PrintStream(new FileOutputStream(method + .name() + + "." + next++ + ".cfg")); + print(out); + + } catch (final 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(final PrintStream out) { + printGraph(new PrintWriter(out, true)); + } + + public void printGraph(final PrintWriter out) { + printGraph(out, "cfg"); + } + + public void printGraph(final PrintWriter out, final String name) { + 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(final Object obj) { + super.print(obj); + super.print("\\n"); + } + + public void visitBlock(final Block block) { + super + .print(" " + + block.label() + + " [shape=box,fontname=\"Courier\",fontsize=6,label=\""); + block.visitChildren(this); + super.print("\"];\n"); + + final Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + super.print(" " + block.label() + " -> " + succ.label()); + + 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(final TreeVisitor visitor) { + final List list = preOrder(); + + if (!visitor.reverse()) { + final ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + block.visit(visitor); + } + + } else { + final ListIterator iter = list.listIterator(list.size()); + + while (iter.hasPrevious()) { + final Block block = (Block) iter.previous(); + block.visit(visitor); + } + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitFlowGraph(this); + } + + /** + * Returns the method editor for the method modeled by this graph. + */ + public MethodEditor 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. + */ + private void removeCriticalEdges() { + // The critical edges + final List edges = new LinkedList(); + + final 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()) { + final 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; + } + + final Iterator preds = preds(dst).iterator(); + + while (preds.hasNext()) { + final 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 }); + } + } + + final 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()) { + final Block[] edge = (Block[]) e.next(); + final Block v = edge[0]; + final 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(final Block src, final 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; + } + + final Block newBlock = newBlock(); + + // Insert in the trace before the dst. + trace.add(trace.indexOf(dst), newBlock); + + final Tree tree = new Tree(newBlock, src.tree().stack()); + newBlock.setTree(tree); + + tree.addInstruction(new Instruction(Opcode.opcx_goto, dst.label())); + + 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. + final JumpStmt newJump = (JumpStmt) newBlock.tree().lastStmt(); + + final Iterator e = handlers.values().iterator(); + + while (e.hasNext()) { + final 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. + */ + private 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. + // + final Iterator entries = subroutines.values().iterator(); + + ENTRIES: while (entries.hasNext()) { + final Subroutine entrySub = (Subroutine) entries.next(); + + // An entry block of a subroutine + final 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; + + final Iterator returns = subroutines.values().iterator(); + + RETURNS: while (returns.hasNext()) { + returnSub = (Subroutine) returns.next(); + + if (returnSub == entrySub) { + continue; + } + + final Iterator paths = returnSub.paths().iterator(); + + while (paths.hasNext()) { + final 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 (FlowGraph.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. + // + final int traceIndex = trace.indexOf(block); + + Tree tree; + + final 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); + + tree + .addInstruction(new Instruction(Opcode.opcx_goto, block + .label())); + + addEdge(newEntry, block); + + final Iterator paths = entrySub.paths().iterator(); + + while (paths.hasNext()) { + final Block[] path = (Block[]) paths.next(); + removeEdge(path[0], block); + addEdge(path[0], newEntry); + path[0].visit(new ReplaceTarget(block, newEntry)); + } + + setSubEntry(entrySub, newEntry); + + final 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); + + tree + .addInstruction(new Instruction(Opcode.opcx_goto, block + .label())); + + returnSub.exit().visit(new ReplaceTarget(block, newTarget)); + ((JsrStmt) returnSubCaller.tree().lastStmt()).setFollow(newTarget); + + addEdge(newTarget, block); + addEdge(returnSub.exit(), newTarget); + removeEdge(returnSub.exit(), block); + + final JumpStmt entryJump = (JumpStmt) newEntry.tree().lastStmt(); + final JumpStmt targetJump = (JumpStmt) newTarget.tree().lastStmt(); + + final 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()) { + final 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). + *

+ */ + private void buildSpecialTrees(final Map catchBodies, final Map labelPos) { + 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 (method.codeLength() > 0) { + tree.initLocals(methodParams(method)); + tree.addInstruction(new Instruction(Opcode.opcx_goto, method + .firstBlock())); + + // (pr) + if (catchBodies != null) { + addHandlerEdges(iniBlock, catchBodies, labelPos, null, + new HashSet()); + } + } + } + + /** + * 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 labelPos + * Maps Labels to their offset in the code (needed for + * buildTreeForBlock) + * @param sub + * The current Subroutine we're in (needed for + * buildTreeForBlock). + */ + private void addHandlerEdges(final Block block, final Map catchBodies, + final Map labelPos, final Subroutine sub, final Set visited) { + // (pr) + if (visited.contains(block)) { + return; + } + visited.add(block); + + final Tree tree = block.tree(); + + Assert.isTrue(tree != null); + + final Iterator hiter = handlers.values().iterator(); + + // Iterate over every Handler object created for this FlowGraph + while (hiter.hasNext()) { + final 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 { + final Iterator succs = succs(block).iterator(); + + while (succs.hasNext()) { + final 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) { + final Block catchBlock = handler.catchBlock(); + + final JumpStmt jump = (JumpStmt) tree.lastStmt(); + + 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. + + final Block catchBody = (Block) catchBodies.get(catchBlock); + Assert.isTrue(catchBody != null); + + if (catchBody.tree() == null) { + final OperandStack s = new OperandStack(); + s.push(new StackExpr(0, Type.THROWABLE)); + + buildTreeForBlock(catchBody, s, sub, labelPos, catchBodies); + } + // (pr) + // if(!handler.catchBlock.equals(block)) { + addHandlerEdges(catchBlock, catchBodies, labelPos, 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 labelPos + * A mapping between Labels and their offset into the code + * @param catchBodies + * A mapping between "catch blocks" and "catch bodies" + */ + private void buildTreeForBlock(final Block block, final OperandStack stack, + final Subroutine sub, final Map labelPos, final Map catchBodies) { + if (block.tree() != null) { + return; + } + + final Tree tree = new Tree(block, stack); + block.setTree(tree); + + final Integer start = (Integer) labelPos.get(block.label()); + Integer targetStart; + + final ListIterator iter = method.code().listIterator( + start.intValue() + 1); + + CODE: + // Iterate over the code in the method... + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + 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.isJsr() || inst.isConditionalJump()) { + int save = 0; + + while (iter.hasNext()) { + final Object obj = iter.next(); + save++; + + if (obj instanceof Label) { + if (((Label) obj).startsBlock()) { + next = (Block) getNode(obj); + + while (save-- > 0) { + iter.previous(); + } + + break; + } + + } else { + throw new RuntimeException(inst + + " not followed by a label: " + obj + " (" + + obj.getClass() + ")"); + } + } + } + + if (inst.opcodeClass() == Opcode.opcx_astore) { + // We need the current subroutine in case this is a + // returnAdress store. + tree.addInstruction(inst, sub); + + } else if (inst.isRet()) { + sub.setExit(block); + tree.addInstruction(inst, sub); + + final 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()) { + final Block[] path = (Block[]) paths.next(); + addEdge(block, path[1]); + } + + break CODE; + + } else if (inst.isThrow() || inst.isReturn()) { + tree.addInstruction(inst); + addEdge(block, snkBlock); + break CODE; + + } else if (inst.isJsr()) { + Assert.isTrue(next != null, inst + + " not followed by a block"); + + tree.addInstruction(inst, next); + + final Label label = (Label) inst.operand(); + + target = (Block) getNode(label); + Assert.isTrue(target != null, inst + " target not found"); + + final Subroutine nextSub = labelSub(label); + setSubEntry(nextSub, target); + + buildTreeForBlock(target, tree.stack(), nextSub, labelPos, + catchBodies); + addEdge(block, target); + + if (nextSub.exit() != null) { + buildTreeForBlock(next, nextSub.exit().tree().stack(), + sub, labelPos, catchBodies); + addEdge(nextSub.exit(), next); + } + + break CODE; + + } else if (inst.isGoto()) { + tree.addInstruction(inst); + + final Label label = (Label) inst.operand(); + + target = (Block) getNode(label); + Assert.isTrue(target != null, inst + " target not found"); + + addEdge(block, target); + + buildTreeForBlock(target, tree.stack(), sub, labelPos, + catchBodies); + + break CODE; + + } else if (inst.isConditionalJump()) { + Assert.isTrue(next != null, inst + + " not followed by a block"); + + tree.addInstruction(inst, next); + + final Label label = (Label) inst.operand(); + + target = (Block) getNode(label); + Assert.isTrue(target != null, inst + " target not found"); + + addEdge(block, target); + buildTreeForBlock(target, tree.stack(), sub, labelPos, + catchBodies); + + addEdge(block, next); + buildTreeForBlock(next, tree.stack(), sub, labelPos, + catchBodies); + + break CODE; + + } else if (inst.isSwitch()) { + tree.addInstruction(inst); + + final Switch sw = (Switch) inst.operand(); + + target = (Block) getNode(sw.defaultTarget()); + + addEdge(block, target); + + buildTreeForBlock(target, tree.stack(), sub, labelPos, + catchBodies); + + for (int j = 0; j < sw.targets().length; j++) { + target = (Block) getNode(sw.targets()[j]); + + addEdge(block, target); + + targetStart = (Integer) labelPos.get(target.label()); + + buildTreeForBlock(target, tree.stack(), sub, labelPos, + catchBodies); + } + + break CODE; + + } else { + tree.addInstruction(inst); + } + + } else if (ce instanceof Label) { + final Label label = (Label) ce; + + if (label.startsBlock()) { + tree + .addInstruction(new Instruction(Opcode.opcx_goto, + label)); + + final Block next = (Block) getNode(label); + + Assert.isTrue(next != null, "Block for " + label + + " not found"); + + addEdge(block, next); + buildTreeForBlock(next, tree.stack(), sub, labelPos, + catchBodies); + break CODE; + } + + tree.addLabel(label); + } + } + + addHandlerEdges(block, catchBodies, labelPos, sub, new HashSet()); + } + + /** + * Returns an ArrayList of the parameters of a method, including the + * receiver (non-static methods only). + * + * @param method + * The method. + */ + private ArrayList methodParams(final MethodEditor method) { + final ArrayList locals = new ArrayList(); + + int index = 0; + + if (!method.isStatic()) { + // Add the this pointer to the locals. + final Type type = method.declaringClass().type(); + final LocalVariable var = method.paramAt(index++); + locals.add(new LocalExpr(var.index(), type)); + } + + final Type[] paramTypes = method.type().indexedParamTypes(); + + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] != null) { + final LocalVariable var = method.paramAt(index); + locals.add(new LocalExpr(var.index(), paramTypes[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.clearCode(); + + final CodeGenerator codegen = new CodeGenerator(method); + visit(codegen); + + final Label endLabel = method.newLabel(); + method.addLabel(endLabel); + + // 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. + final Iterator iter = catchBlocks.iterator(); + + while (iter.hasNext()) { + final Block catchBlock = (Block) iter.next(); + + final Handler handler = (Handler) handlers.get(catchBlock); + Assert.isTrue(handler != null); + + Type type = handler.catchType(); + + if (type.isNull()) { + type = null; + } + + Block begin = null; + + final Iterator blocks = trace().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + if (handler.protectedBlocks().contains(block)) { + if (begin == null) { + begin = block; + } + } else if (begin != null) { + final TryCatch tc = new TryCatch(begin.label(), block + .label(), catchBlock.label(), type); + + method.addTryCatch(tc); + + 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(final Collection blocks) { + return idf(blocks, false); + } + + /** + * Returns the iterated postdominance frontier for several basic blocks. + * + * @see Block#pdomFrontier + */ + public Collection iteratedPdomFrontier(final 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(final Collection blocks, boolean reverse) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + final HashSet idf = new HashSet(); + + final HashSet inWorklist = new HashSet(blocks); + final LinkedList worklist = new LinkedList(inWorklist); + + while (!worklist.isEmpty()) { + final Block block = (Block) worklist.removeFirst(); + + Collection df; + + if (!reverse) { + df = block.domFrontier(); + } else { + df = block.pdomFrontier(); + } + + final Iterator iter = df.iterator(); + + while (iter.hasNext()) { + final 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(final Object obj) { + return obj == srcBlock; + } + + public Iterator iterator() { + return new Iterator() { + Object next = srcBlock; + + public boolean hasNext() { + return next != null; + } + + public Object next() { + final 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(final Object obj) { + return obj == snkBlock; + } + + public Iterator iterator() { + return new Iterator() { + Object next = snkBlock; + + public boolean hasNext() { + return next != null; + } + + public Object next() { + final 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(final Object key) { + final 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(final 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()) { + final Handler handler = (Handler) iter.next(); + handler.protectedBlocks().remove(block); + } + + iter = subroutines.values().iterator(); + + while (iter.hasNext()) { + final 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()) { + final Stmt s = (Stmt) iter.next(); + + if (s instanceof LabelStmt) { + final Label label = ((LabelStmt) s).label(); + label.setStartsBlock(false); + iniBlock.tree().addStmt(new LabelStmt(label)); + } + + s.cleanup(); + } + } + + super.removeNode(block.label()); + } + + /** + * Returns the blocks that a given block dominates. + */ + public Collection domChildren(final Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.domChildren(); + } + + /** + * Returns the Block that dominates a given block. + */ + public Block domParent(final 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(final 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(final Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + if ((block == srcBlock) || (block.blockType() != Block.NON_HEADER)) { + final LoopNode loop = (LoopNode) loopTree.getNode(block); + Assert.isTrue(loop != null, "no loop for " + block); + return loop.depth; + } + + if (block.header() != null) { + final 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(final Block block) { + if (loopEdgeModCount != edgeModCount) { + buildLoopTree(); + } + + if ((block == srcBlock) || (block.blockType() != Block.NON_HEADER)) { + final LoopNode loop = (LoopNode) loopTree.getNode(block); + Assert.isTrue(loop != null, "no loop for " + block); + return loop.level; + } + + if (block.header() != null) { + final 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(final 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(final Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.pdomChildren(); + } + + /** + * Returns the postdominator parent of a given block. + * + * @see Block#pdomParent + */ + public Block pdomParent(final Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.pdomParent(); + } + + /** + * Returns the dominance frontier of a given block. + * + * @see Block#domFrontier + */ + public Collection domFrontier(final Block block) { + if (domEdgeModCount != edgeModCount) { + computeDominators(); + } + + return block.domFrontier(); + } + + /** + * Returns the postdominance frontier of a given block. + * + * @see Block#pdomFrontier + */ + public Collection pdomFrontier(final 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. + */ + class LoopNode extends GraphNode { + Block header; + + int depth; + + int level; + + Set elements; + + public LoopNode(final Block header) { + this.header = header; + this.depth = 1; + this.level = 1; + this.elements = new HashSet(); + 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); + } +} 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..ea8608d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/Handler.java @@ -0,0 +1,79 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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; + + Type type; + + /** + * Constructor. + * + * @param catchBlock + * The block of code that handles an exception + * @param type + * The type of exception that is thrown + */ + public Handler(final Block catchBlock, final Type type) { + this.protectedBlocks = new HashSet(); + this.catchBlock = catchBlock; + this.type = type; + } + + /** + * Returns a Collection of the "try" blocks. + */ + public Collection protectedBlocks() { + return protectedBlocks; + } + + public void setCatchBlock(final 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..01825ad --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/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. +# +# 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 + +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..63d90e2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/ReplaceTarget.java @@ -0,0 +1,167 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.util.*; + +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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(final Block oldDst, final Block newDst) { + this.oldDst = oldDst; + this.newDst = newDst; + } + + public void visitTree(final Tree tree) { + final Stmt last = tree.lastStmt(); + + if (last instanceof JumpStmt) { + final 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(final 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(final RetStmt stmt) { + final Iterator paths = stmt.sub().paths().iterator(); + + while (paths.hasNext()) { + final 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(final 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(final 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); + } + } + + final 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(final 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..56e4b1e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/Subroutine.java @@ -0,0 +1,259 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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; + + LocalVariable returnAddress; // This Subroutine's return address + + /** + * Constructor. + * + * @param graph + * The CFG containing the block. + */ + public Subroutine(final 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 LocalVariable 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(final LocalVariable returnAddress) { + this.returnAddress = returnAddress; + } + + /** + * 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(final Block block) { + for (int i = paths.size() - 1; i >= 0; i--) { + final 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(final Block callerBlock, final Block returnBlock) { + for (int i = 0; i < paths.size(); i++) { + final 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(final Block callerBlock, final Block returnBlock) { + for (int i = 0; i < paths.size(); i++) { + final 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(final Block block) { + for (int i = 0; i < paths.size(); i++) { + final 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(final Block block) { + for (int i = 0; i < paths.size(); i++) { + final 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(final Block entry) { + this.entry = entry; + } + + /** + * Sets the exit Block for this Subroutine. + */ + public void setExit(final 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(final PrintStream out) { + out.println(" " + entry); + + final Iterator e = paths().iterator(); + + while (e.hasNext()) { + final 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..72cb0a1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/cfg/VerifyCFG.java @@ -0,0 +1,384 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.cfg; + +import java.util.*; + +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 { + 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? + + /** + * 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(final boolean checkValueNumbers) { + this.checkValueNumbers = checkValueNumbers; + } + + /** + * 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(final FlowGraph cfg) { + if (FlowGraph.DEBUG) { + System.out.println("Verifying CFG for " + cfg.method()); + } + + this.cfg = cfg; + + uses = new HashSet(); + nodes = new HashSet(); + + cfg.visitChildren(this); + + final Iterator e = uses.iterator(); + + while (e.hasNext()) { + final 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(final Block block) { + Assert.isTrue(block.graph() == cfg, block + " is not in the CFG"); + + Iterator e; + + final 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) { + final HashSet handlerPreds = new HashSet(); + + e = handler.protectedBlocks().iterator(); + + while (e.hasNext()) { + final Block prot = (Block) e.next(); + handlerPreds.add(prot); + handlerPreds.addAll(cfg.preds(prot)); + } + + final HashSet extra = new HashSet(cfg.preds(block)); + extra.removeAll(handlerPreds); + final HashSet missing = new HashSet(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()) { + final 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()) { + final 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(final RetStmt stmt) { + final Set targets = new HashSet(); + + final Iterator iter = stmt.sub().paths().iterator(); + + while (iter.hasNext()) { + final 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(final JsrStmt stmt) { + final Set targets = new HashSet(); + 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(final SwitchStmt stmt) { + final Set targets = new HashSet(); + + 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(final IfStmt stmt) { + final Set targets = new HashSet(); + 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(final GotoStmt stmt) { + final Set targets = new HashSet(); + 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(final Block block, final Set targets) { + Assert.isTrue(targets.size() == cfg.succs(block).size(), block + + " has succs " + cfg.succs(block) + " != " + targets + " in " + + block.tree().lastStmt()); + + final Iterator iter = targets.iterator(); + + while (iter.hasNext()) { + final 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(final 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().isVoid()) { + 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(final 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(final Node n) { + children.add(n); + } + }); + + final Iterator e = children.iterator(); + + while (e.hasNext()) { + final 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(final 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(final 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(final 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..64d8cab --- /dev/null +++ b/src/EDU/purdue/cs/bloat/class.mk @@ -0,0 +1,43 @@ +# 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. +# +# 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 + +.SUFFIXES: .java .class + +JAVA_HOME = /gcm/where/jdk/1.3/sparc.Solaris +JAVAC = $(JAVA_HOME)/bin/javac +JFLAGS = -g +CLASSPATH = $(JAVA_HOME)/lib/classes.zip + +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/.cvsignore b/src/EDU/purdue/cs/bloat/codegen/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/codegen/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file 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..1a00607 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/codegen/CodeGenerator.java @@ -0,0 +1,2339 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.codegen; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 implements Opcode { + 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; + + protected MethodEditor method; + + protected Set visited; + + protected Map postponedInstructions; + + protected Block next; + + protected int stackHeight; // The current height of the stack + + StackOptimizer currentSO; // object used to determine where to apply + + // stack optimization + + /** + * Constructor. + * + * @param method + * The method for which bytecode is generated. + */ + public CodeGenerator(final MethodEditor method) { + this.method = method; + this.postponedInstructions = new HashMap(); + } + + /** + * 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 HashSet(); + visited.add(cfg.source()); + visited.add(cfg.sink()); + + final Iterator e = cfg.trace().iterator(); + + Assert.isTrue(e.hasNext(), "trace is empty"); + + stackHeight = 0; // At beginning of method stack has height 0 + + Block 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); + } + + // 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() { + boolean startsBlock = true; + + public void visitLabelStmt(final LabelStmt stmt) { + stmt.label().setStartsBlock(startsBlock); + startsBlock = false; + } + + public void visitStmt(final Stmt stmt) { + } + }); + + // Generate the code for each block + visited.add(block); + // currentSO is the StackOptimizer object that discerns + // where dups may be used instead of loads + if (CodeGenerator.OPT_STACK) { + currentSO = block.stackOptimizer(); + } + 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 TryCatch objects to represent the protected regions. + + final Iterator iter = cfg.catchBlocks().iterator(); + + while (iter.hasNext()) { + final Block catchBlock = (Block) iter.next(); + final Handler handler = (Handler) cfg.handlersMap().get(catchBlock); + + Type type = handler.catchType(); + + if (type.isNull()) { + type = null; + } + + // First block in protected block + Block begin = null; + + final Iterator blocks = cfg.trace().iterator(); + + while (blocks.hasNext()) { + block = (Block) blocks.next(); + + if (handler.protectedBlocks().contains(block)) { + if (begin == null) { + begin = block; + } + + } else if (begin != null) { + // The block is no longer protected, its the end of the + // protected region + final TryCatch tc = new TryCatch(begin.label(), block + .label(), catchBlock.label(), type); + method.addTryCatch(tc); + + begin = null; + } + } + } + } + + /** + * 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(final 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) { + final 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) { + final JumpStmt oldJump = (JumpStmt) exit.tree() + .lastStmt(); + final JumpStmt jump = new GotoStmt(stmt.follow()); + jump.catchTargets().addAll(oldJump.catchTargets()); + oldJump.replaceWith(jump); + } + + final 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(final Tree tree) { + iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + final Stmt s = (Stmt) iter.next(); + + if (s instanceof AddressStoreStmt) { + final AddressStoreStmt store = (AddressStoreStmt) s; + + if (store.sub() == sub) { + iter.remove(); + } + } + } + } + }); + } + } + + public void visitStmt(final Stmt stmt) { + } + }); + } + + /** + * 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(final Tree tree) { + final Iterator e = tree.stmts().iterator(); + + while (e.hasNext()) { + final 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() { + HashMap seen = new HashMap(); + + public void visitFlowGraph(final FlowGraph graph) { + final Iterator iter = graph.catchBlocks().iterator(); + + // Examine each block that begins an exception handler + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + block.visit(this); + } + } + + public void visitPhiCatchStmt(final PhiCatchStmt phi) { + final LocalExpr target = (LocalExpr) phi.target(); + final int index = target.index(); + + final 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()) { + final LocalExpr expr = (LocalExpr) iter.next(); + final LocalExpr def = (LocalExpr) expr.def(); + + if (def == null) { + continue; + } + + if (CodeGenerator.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(final StoreExpr expr) { + if (CodeGenerator.DEBUG) { + System.out.println(" merging with " + + expr); + } + + final Expr lhs = expr.target(); + final 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(final 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 + + final Stmt stmt = (Stmt) def.parent(); + final 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) + final StoreExpr p = (StoreExpr) def.parent(); + final 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 + final StoreExpr store = new StoreExpr( + (LocalExpr) target.clone(), rhs, rhs.type()); + + p.visit(new ReplaceVisitor(rhs, store)); + } + } + } + } + + public void visitStmt(final 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. + + final Iterator iter = cfg.trace().iterator(); + + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + + if (block == cfg.sink()) { + continue; + } + + block.visit(new TreeVisitor() { + public void visitPhiJoinStmt(final 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. + + final Iterator preds = cfg.preds(stmt.block()).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + + final Expr operand = stmt.operandAt(pred); + + if ((stmt.target() instanceof LocalExpr) + && (operand instanceof LocalExpr)) { + + final LocalExpr t = (LocalExpr) stmt.target(); + final 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(final Expr expr) { + StackExpr var = tree.newStack(expr.type()); + var.setValueNumber(expr.valueNumber()); + + final Node p = expr.parent(); + expr.setParent(null); + p.visit(new ReplaceVisitor(expr, var)); + + var = (StackExpr) var.clone(); + final StoreExpr store = new StoreExpr(var, + expr, expr.type()); + store.setValueNumber(expr.valueNumber()); + + final Stmt storeStmt = new ExprStmt(store); + storeStmt.setValueNumber(expr.valueNumber()); + + tree.addStmtBeforeJump(storeStmt); + } + + public void visitStackExpr(final StackExpr expr) { + } + }); + + final Stmt store = createStore(stmt.target(), operand); + + if (CodeGenerator.DEBUG) { + System.out.println("insert for " + stmt + " " + + store + " in " + pred); + } + + tree.addStmtBeforeJump(store); + } + } + + public void visitStmt(final 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) { + final Set emptyBlocks = new HashSet(); + + Iterator e = cfg.nodes().iterator(); + + BLOCKS: while (e.hasNext()) { + final Block block = (Block) e.next(); + + // Collect any blocks that contain only gotos, + // jsrs, rets, or labels. + final Iterator stmts = block.tree().stmts().iterator(); + + while (stmts.hasNext()) { + final 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. + final Set empty = new HashSet(emptyBlocks); + empty.removeAll(cfg.iteratedPdomFrontier(cfg.nodes())); + + e = empty.iterator(); + + while (e.hasNext()) { + final Block block = (Block) e.next(); + + if (CodeGenerator.DEBUG) { + System.out.println("removing empty " + block); + } + + final 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 + final Block target = ((GotoStmt) last).target(); + + final Iterator preds = new ImmutableIterator(cfg + .preds(block)); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + final 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 + + final Iterator preds = new ImmutableIterator(cfg + .preds(block)); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + final 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: ... + + final JsrStmt stmt = (JsrStmt) predLast; + + final 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). + + final 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 + final Iterator preds = new ImmutableIterator(cfg + .preds(block)); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + Assert.isTrue(pred != cfg.source()); + + final Stmt predLast = pred.tree().lastStmt(); + + if (predLast instanceof GotoStmt) { + final JsrStmt stmt = (JsrStmt) last; + + final 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 { + throw new RuntimeException(); + } + } + + if (changed) { + cfg.removeUnreachable(); + + // Remove any empty blocks that we've already deleted. + emptyBlocks.retainAll(cfg.nodes()); + } + } + } + + /** + * 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(final 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. + final Iterator e = cfg.subroutines().iterator(); + + while (e.hasNext()) { + final Subroutine sub = (Subroutine) e.next(); + final LocalVariable var = alloc.newLocal(Type.ADDRESS); + sub.setReturnAddress(var); + } + } + + /** + * Create a ExprStmt that initializes a target variable to a default value + * based on the type of the target. + */ + protected Stmt createUndefinedStore(final VarExpr target) { + if (target.type().isReference()) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr(null, + Type.OBJECT), target.type())); + } + + if (target.type().isIntegral()) { + return new ExprStmt(new StoreExpr(target, new ConstantExpr( + new Integer(0), Type.INTEGER), 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, final Expr source) { + target = (VarExpr) target.clone(); + + // 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(), 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(final Expr expr) { + throw new RuntimeException("Unhandled expression type: " + + expr.getClass().getName()); + } + + public void visitExprStmt(final 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().isVoid()) { + if (stmt.expr().type().isWide()) { + method.addInstruction(Opcode.opcx_pop2); + stackHeight -= 2; + + } else { + method.addInstruction(Opcode.opcx_pop); + stackHeight -= 1; + } + } + } + } + + public void visitInitStmt(final InitStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + } + + public void visitGotoStmt(final GotoStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + genPostponed(stmt); + + final Block target = stmt.target(); + + if (target != next) { + method.addInstruction(Opcode.opcx_goto, stmt.target().label()); + } + } + + public void visitIfCmpStmt(final IfCmpStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + final Block t = stmt.trueTarget(); + final 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" + method.addInstruction(Opcode.opcx_goto, f.label()); + } + } + + private void genIfCmpStmt(final IfCmpStmt stmt) { + int opcode; + + stmt.visitChildren(this); + + genPostponed(stmt); + + final int cmp = stmt.comparison(); + + if (stmt.left().type().isReference()) { + Assert.isTrue(stmt.right().type().isReference(), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + opcode = Opcode.opcx_if_acmpeq; + break; + case IfStmt.NE: + opcode = Opcode.opcx_if_acmpne; + break; + default: + throw new RuntimeException(); + } + + } else { + Assert.isTrue(stmt.left().type().isIntegral(), + "Illegal statement: " + stmt); + Assert.isTrue(stmt.right().type().isIntegral(), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + opcode = Opcode.opcx_if_icmpeq; + break; + case IfStmt.NE: + opcode = Opcode.opcx_if_icmpne; + break; + case IfStmt.GT: + opcode = Opcode.opcx_if_icmpgt; + break; + case IfStmt.GE: + opcode = Opcode.opcx_if_icmpge; + break; + case IfStmt.LT: + opcode = Opcode.opcx_if_icmplt; + break; + case IfStmt.LE: + opcode = Opcode.opcx_if_icmple; + break; + default: + throw new RuntimeException(); + } + } + + method.addInstruction(opcode, stmt.trueTarget().label()); + stackHeight -= 2; + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + final Block t = stmt.trueTarget(); + final 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); + method.addLabel(method.newLabelTrue()); // Tom added "True" + method.addInstruction(Opcode.opcx_goto, f.label()); + } + } + + private void genIfZeroStmt(final IfZeroStmt stmt) { + int opcode; + + stmt.expr().visit(this); + + genPostponed(stmt); + + final int cmp = stmt.comparison(); + + if (stmt.expr().type().isReference()) { + switch (cmp) { + case IfStmt.EQ: + opcode = Opcode.opcx_ifnull; + break; + case IfStmt.NE: + opcode = Opcode.opcx_ifnonnull; + break; + default: + throw new RuntimeException(); + } + + } else { + Assert.isTrue(stmt.expr().type().isIntegral(), + "Illegal statement: " + stmt); + + switch (cmp) { + case IfStmt.EQ: + opcode = Opcode.opcx_ifeq; + break; + case IfStmt.NE: + opcode = Opcode.opcx_ifne; + break; + case IfStmt.GT: + opcode = Opcode.opcx_ifgt; + break; + case IfStmt.GE: + opcode = Opcode.opcx_ifge; + break; + case IfStmt.LT: + opcode = Opcode.opcx_iflt; + break; + case IfStmt.LE: + opcode = Opcode.opcx_ifle; + break; + default: + throw new RuntimeException(); + } + } + method.addInstruction(opcode, stmt.trueTarget().label()); + stackHeight -= 1; + } + + public void visitLabelStmt(final LabelStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + genPostponed(stmt); + + method.addLabel(stmt.label()); + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + genPostponed(stmt); + + if (stmt.kind() == MonitorStmt.ENTER) { + method.addInstruction(Opcode.opcx_monitorenter); + stackHeight -= 1; + + } else if (stmt.kind() == MonitorStmt.EXIT) { + method.addInstruction(Opcode.opcx_monitorexit); + stackHeight -= 1; + + } else { + throw new IllegalArgumentException(); + } + } + + public void visitPhiStmt(final PhiStmt stmt) { + throw new RuntimeException("Cannot generate code for " + stmt); + } + + public void visitRCExpr(final 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) { + final ArrayRefExpr p = (ArrayRefExpr) parent; + + if (expr == p.array()) { + if (p.isDef()) { + // a[i] := r + // Stack at the xastore: ... a i r + postpone = new Instruction(Opcode.opcx_rc, new Integer(p + .type().stackHeight() + 1)); + } else { + // use a[i] + // Stack at the xaload: ... a i + postpone = new Instruction(Opcode.opcx_rc, new Integer(1)); + } + } + + } else if (parent instanceof CallMethodExpr) { + final CallMethodExpr p = (CallMethodExpr) parent; + + if (expr == p.receiver()) { + // a.m(b, c) + // Stack at the invoke: ... a b c + final MemberRef method = p.method(); + final int depth = method.nameAndType().type().stackHeight(); + postpone = new Instruction(Opcode.opcx_rc, new Integer(depth)); + } + + } else if (parent instanceof FieldExpr) { + final FieldExpr p = (FieldExpr) parent; + + if (expr == p.object()) { + if (p.isDef()) { + // a.b := r + // Stack at the putfield: ... a r + postpone = new Instruction(Opcode.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. + final StackExpr stackVar = (StackExpr) expr.expr(); + depth = stackHeight - stackVar.index() - 1; + } + + method.addInstruction(Opcode.opcx_rc, new Integer(depth)); + + } else { + postponedInstructions.put(parent, postpone); + } + } + + public void visitUCExpr(final UCExpr expr) { + expr.visitChildren(this); + + if (true) { + return; + } + + genPostponed(expr); + + // Move the uc forward as far as possible. + Instruction postpone = null; + + final 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) { + final 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(Opcode.opcx_aupdate, + new Integer(p.type().stackHeight())); + + } else if (expr.kind() == UCExpr.SCALAR) { + postpone = new Instruction(Opcode.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. + final StackExpr stackVar = (StackExpr) expr.expr(); + depth = stackHeight - stackVar.index() - 1; + } + + if (expr.kind() == UCExpr.POINTER) { + method.addInstruction(Opcode.opcx_aupdate, new Integer(depth)); + } else if (expr.kind() == UCExpr.SCALAR) { + method.addInstruction(Opcode.opcx_supdate, new Integer(depth)); + } else { + throw new RuntimeException(); + } + + } else { + postponedInstructions.put(parent, postpone); + } + } + + public void visitRetStmt(final RetStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + genPostponed(stmt); + + final Subroutine sub = stmt.sub(); + Assert.isTrue(sub.returnAddress() != null); + method.addInstruction(Opcode.opcx_ret, sub.returnAddress()); + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + genPostponed(stmt); + + final Type type = stmt.expr().type(); + + // Stack should be empty after return + + if (type.isReference()) { + method.addInstruction(Opcode.opcx_areturn); + stackHeight = 0; + } else if (type.isIntegral()) { + method.addInstruction(Opcode.opcx_ireturn); + stackHeight = 0; + } else if (type.equals(Type.LONG)) { + method.addInstruction(Opcode.opcx_lreturn); + stackHeight = 0; + } else if (type.equals(Type.FLOAT)) { + method.addInstruction(Opcode.opcx_freturn); + stackHeight = 0; + } else if (type.equals(Type.DOUBLE)) { + method.addInstruction(Opcode.opcx_dreturn); + stackHeight = 0; + } + } + + public void visitReturnStmt(final ReturnStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + genPostponed(stmt); + + stmt.visitChildren(this); + method.addInstruction(Opcode.opcx_return); + + // Stack height is zero after return + stackHeight = 0; + } + + public void visitStoreExpr(final StoreExpr expr) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + expr); + } + + final MemExpr lhs = expr.target(); + final 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()) + + && (!CodeGenerator.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) && lhs.type().isIntegral()) { + Integer value = null; + // eliminate the store if the both sides have the same target + + final int index = ((LocalExpr) lhs).index(); + + if (rhs instanceof ArithExpr) { + final ArithExpr arith = (ArithExpr) rhs; + + final Expr left = arith.left(); + final Expr right = arith.right(); + + if ((left instanceof LocalExpr) + && (index == ((LocalExpr) left).index()) + && (right instanceof ConstantExpr)) { + + final 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. + + final 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) { + final 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 (CodeGenerator.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. + method.addInstruction(Opcode.opcx_ldc, + new Integer(0)); + method.addInstruction(Opcode.opc_dup_x2); + method.addInstruction(Opcode.opc_pop); + stackHeight += 1; + } + for (int i = 0; i < dup_x1s; i++) { + method.addInstruction(Opcode.opcx_ldc, + new Integer(0)); + method.addInstruction(Opcode.opc_swap); + stackHeight += 1; + } + for (int i = 0; i < dups; i++) { + method.addInstruction(Opcode.opcx_ldc, + new Integer(0)); + stackHeight += 1; + } + } + + return; + + } else if ((short) incr == incr) { + // Only generate an iinc if the increment fits in + // a short. + method.addInstruction(Opcode.opcx_iinc, new IncOperand( + new LocalVariable(index), incr)); + + if (CodeGenerator.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.addInstruction(Opcode.opcx_istore, + new LocalVariable(((LocalExpr) lhs) + .index())); + method.addInstruction(Opcode.opc_dup_x2); + method.addInstruction(Opcode.opc_pop); + stackHeight += 1; + } + for (int i = 0; i < dup_x1s; i++) { + method.addInstruction(Opcode.opcx_iload, + new LocalVariable(((LocalExpr) lhs) + .index())); + method.addInstruction(Opcode.opc_swap); + stackHeight += 1; + } + for (int i = 0; i < dups; i++) { + method.addInstruction(Opcode.opcx_iload, + new LocalVariable(((LocalExpr) lhs) + .index())); + 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().isWide()) { + method.addInstruction(Opcode.opcx_dup2_x2); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opcx_dup_x2); + stackHeight += 1; + } + + } else if (lhs instanceof FieldExpr) { + // object rhs --> rhs object rhs + if (rhs.type().isWide()) { + method.addInstruction(Opcode.opcx_dup2_x1); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opcx_dup_x1); + stackHeight += 1; + } + + } else { + // rhs --> rhs rhs + if (rhs.type().isWide()) { + method.addInstruction(Opcode.opcx_dup2); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opcx_dup); + stackHeight += 1; + } + } + } + + genPostponed(expr); + lhs.visitOnly(this); + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + genPostponed(stmt); + + final Subroutine sub = stmt.sub(); + Assert.isTrue(sub.returnAddress() != null); + method.addInstruction(Opcode.opcx_astore, sub.returnAddress()); + stackHeight -= 1; + } + + public void visitJsrStmt(final JsrStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + genPostponed(stmt); + + final Block entry = stmt.sub().entry(); + method.addInstruction(Opcode.opcx_jsr, entry.label()); + stackHeight += 1; + + if (stmt.follow() != next) { + method.addLabel(method.newLabelTrue()); + method.addInstruction(Opcode.opcx_goto, stmt.follow().label()); + } + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + genPostponed(stmt); + + final Label[] targets = new Label[stmt.targets().length]; + + for (int i = 0; i < targets.length; i++) { + targets[i] = stmt.targets()[i].label(); + } + + method.addInstruction(Opcode.opcx_switch, new Switch(stmt + .defaultTarget().label(), targets, stmt.values())); + stackHeight -= 1; + } + + public void visitStackManipStmt(final 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: + method.addInstruction(Opcode.opcx_swap); + break; + case StackManipStmt.DUP: + method.addInstruction(Opcode.opcx_dup); + stackHeight += 1; + break; + case StackManipStmt.DUP_X1: + method.addInstruction(Opcode.opcx_dup_x1); + stackHeight += 1; + break; + case StackManipStmt.DUP_X2: + method.addInstruction(Opcode.opcx_dup_x2); + stackHeight += 1; + break; + case StackManipStmt.DUP2: + method.addInstruction(Opcode.opcx_dup2); + stackHeight += 2; + break; + case StackManipStmt.DUP2_X1: + method.addInstruction(Opcode.opcx_dup2_x1); + stackHeight += 2; + break; + case StackManipStmt.DUP2_X2: + method.addInstruction(Opcode.opcx_dup2_x2); + stackHeight += 2; + break; + } + } + + public void visitThrowStmt(final ThrowStmt stmt) { + if (CodeGenerator.DEBUG) { + System.out.println("code for " + stmt); + } + + stmt.visitChildren(this); + + genPostponed(stmt); + + method.addInstruction(Opcode.opcx_athrow); + } + + public void visitSCStmt(final SCStmt stmt) { + stmt.visitChildren(this); + genPostponed(stmt); + method.addInstruction(Opcode.opcx_aswizzle); + stackHeight -= 2; + } + + public void visitSRStmt(final SRStmt stmt) { + stmt.visitChildren(this); + genPostponed(stmt); + method.addInstruction(Opcode.opcx_aswrange); + stackHeight -= 3; + } + + public void visitArithExpr(final ArithExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + final int[][] opcode = new int[][] { + { Opcode.opcx_iadd, Opcode.opcx_ladd, Opcode.opcx_fadd, + Opcode.opcx_dadd }, + { Opcode.opcx_iand, Opcode.opcx_land, Opcode.opcx_nop, + Opcode.opcx_nop }, + { Opcode.opcx_idiv, Opcode.opcx_ldiv, Opcode.opcx_fdiv, + Opcode.opcx_ddiv }, + { Opcode.opcx_imul, Opcode.opcx_lmul, Opcode.opcx_fmul, + Opcode.opcx_dmul }, + { Opcode.opcx_ior, Opcode.opcx_lor, Opcode.opcx_nop, + Opcode.opcx_nop }, + { Opcode.opcx_irem, Opcode.opcx_lrem, Opcode.opcx_frem, + Opcode.opcx_drem }, + { Opcode.opcx_isub, Opcode.opcx_lsub, Opcode.opcx_fsub, + Opcode.opcx_dsub }, + { Opcode.opcx_ixor, Opcode.opcx_lxor, Opcode.opcx_nop, + Opcode.opcx_nop }, + { Opcode.opcx_nop, Opcode.opcx_lcmp, Opcode.opcx_nop, + Opcode.opcx_nop }, + { Opcode.opcx_nop, Opcode.opcx_nop, Opcode.opcx_fcmpl, + Opcode.opcx_dcmpl }, + { Opcode.opcx_nop, Opcode.opcx_nop, Opcode.opcx_fcmpg, + Opcode.opcx_dcmpg } }; + + final 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 (expr.left().type().isIntegral()) { + 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: + method.addInstruction(opcode[0][type]); + stackHeight += stackChange[0][type]; + break; + case ArithExpr.AND: + method.addInstruction(opcode[1][type]); + stackHeight += stackChange[1][type]; + break; + case ArithExpr.DIV: + method.addInstruction(opcode[2][type]); + stackHeight += stackChange[2][type]; + break; + case ArithExpr.MUL: + method.addInstruction(opcode[3][type]); + stackHeight += stackChange[3][type]; + break; + case ArithExpr.IOR: + method.addInstruction(opcode[4][type]); + stackHeight += stackChange[4][type]; + break; + case ArithExpr.REM: + method.addInstruction(opcode[5][type]); + stackHeight += stackChange[5][type]; + break; + case ArithExpr.SUB: + method.addInstruction(opcode[6][type]); + stackHeight += stackChange[6][type]; + break; + case ArithExpr.XOR: + method.addInstruction(opcode[7][type]); + stackHeight += stackChange[7][type]; + break; + case ArithExpr.CMP: + method.addInstruction(opcode[8][type]); + stackHeight += stackChange[8][type]; + break; + case ArithExpr.CMPL: + method.addInstruction(opcode[9][type]); + stackHeight += stackChange[9][type]; + break; + case ArithExpr.CMPG: + method.addInstruction(opcode[10][type]); + stackHeight += stackChange[10][type]; + break; + } + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + expr.visitChildren(this); + method.addInstruction(Opcode.opcx_arraylength); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + int opcode; + + if (expr.isDef()) { + if (expr.elementType().isReference()) { + opcode = Opcode.opcx_aastore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.BYTE)) { + opcode = Opcode.opcx_bastore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.CHARACTER)) { + opcode = Opcode.opcx_castore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.SHORT)) { + opcode = Opcode.opcx_sastore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.INTEGER)) { + opcode = Opcode.opcx_iastore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.LONG)) { + opcode = Opcode.opcx_lastore; + stackHeight -= 4; + } else if (expr.elementType().equals(Type.FLOAT)) { + opcode = Opcode.opcx_fastore; + stackHeight -= 3; + } else if (expr.elementType().equals(Type.DOUBLE)) { + opcode = Opcode.opcx_dastore; + stackHeight -= 4; + } else { + throw new IllegalArgumentException( + "Can't generate code for type: " + expr.type() + + " (expr " + expr + ")"); + } + } else { + if (expr.elementType().isReference()) { + opcode = Opcode.opcx_aaload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.BYTE)) { + opcode = Opcode.opcx_baload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.CHARACTER)) { + opcode = Opcode.opcx_caload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.SHORT)) { + opcode = Opcode.opcx_saload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.INTEGER)) { + opcode = Opcode.opcx_iaload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.LONG)) { + opcode = Opcode.opcx_laload; + stackHeight -= 0; + } else if (expr.elementType().equals(Type.FLOAT)) { + opcode = Opcode.opcx_faload; + stackHeight -= 1; + } else if (expr.elementType().equals(Type.DOUBLE)) { + opcode = Opcode.opcx_daload; + stackHeight -= 0; + } else { + throw new IllegalArgumentException( + "Can't generate code for type: " + expr.type() + + " (expr " + expr + ")"); + } + } + + method.addInstruction(opcode); + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + int opcode; + + if (expr.kind() == CallMethodExpr.VIRTUAL) { + opcode = Opcode.opcx_invokevirtual; + } else if (expr.kind() == CallMethodExpr.NONVIRTUAL) { + opcode = Opcode.opcx_invokespecial; + } else if (expr.kind() == CallMethodExpr.INTERFACE) { + opcode = Opcode.opcx_invokeinterface; + } else { + throw new IllegalArgumentException(); + } + + method.addInstruction(opcode, expr.method()); + + // Pop reciever object off stack + stackHeight -= 1; + + // Pop each parameter off stack + final Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) { + stackHeight -= params[i].type().stackHeight(); + } + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + method.addInstruction(Opcode.opcx_invokestatic, expr.method()); + + // Pop each parameter off stack + final Expr[] params = expr.params(); + for (int i = 0; i < params.length; i++) { + stackHeight -= params[i].type().stackHeight(); + } + } + + public void visitCastExpr(final CastExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + if (expr.castType().isReference()) { + method.addInstruction(Opcode.opcx_checkcast, expr.castType()); + return; + } + + final int opType = expr.expr().type().typeCode(); + final int castType = expr.castType().typeCode(); + + switch (opType) { + case Type.BYTE_CODE: + case Type.SHORT_CODE: + case Type.CHARACTER_CODE: + case Type.INTEGER_CODE: + switch (castType) { + case Type.BYTE_CODE: + method.addInstruction(Opcode.opcx_i2b); + return; + case Type.SHORT_CODE: + method.addInstruction(Opcode.opcx_i2s); + return; + case Type.CHARACTER_CODE: + method.addInstruction(Opcode.opcx_i2c); + return; + case Type.INTEGER_CODE: + return; + case Type.LONG_CODE: + method.addInstruction(Opcode.opcx_i2l); + stackHeight += 1; + return; + case Type.FLOAT_CODE: + method.addInstruction(Opcode.opcx_i2f); + return; + case Type.DOUBLE_CODE: + method.addInstruction(Opcode.opcx_i2d); + stackHeight += 1; + return; + } + throw new IllegalArgumentException("Can't generate cast for type " + + Type.getType(castType)); + // new Type(castType)); + + case Type.LONG_CODE: + switch (castType) { + case Type.BYTE_CODE: + method.addInstruction(Opcode.opcx_l2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2b); + return; + case Type.SHORT_CODE: + method.addInstruction(Opcode.opcx_l2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2s); + return; + case Type.CHARACTER_CODE: + method.addInstruction(Opcode.opcx_l2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2c); + return; + case Type.INTEGER_CODE: + method.addInstruction(Opcode.opcx_l2i); + stackHeight -= 1; + return; + case Type.LONG_CODE: + return; + case Type.FLOAT_CODE: + method.addInstruction(Opcode.opcx_l2f); + stackHeight -= 1; + return; + case Type.DOUBLE_CODE: + method.addInstruction(Opcode.opcx_l2d); + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + Type.getType(castType)); + // new Type(castType)); + + case Type.FLOAT_CODE: + switch (castType) { + case Type.BYTE_CODE: + method.addInstruction(Opcode.opcx_f2i); + method.addInstruction(Opcode.opcx_i2b); + return; + case Type.SHORT_CODE: + method.addInstruction(Opcode.opcx_f2i); + method.addInstruction(Opcode.opcx_i2s); + return; + case Type.CHARACTER_CODE: + method.addInstruction(Opcode.opcx_f2i); + method.addInstruction(Opcode.opcx_i2c); + return; + case Type.INTEGER_CODE: + method.addInstruction(Opcode.opcx_f2i); + return; + case Type.LONG_CODE: + method.addInstruction(Opcode.opcx_f2l); + stackHeight += 1; + return; + case Type.FLOAT_CODE: + return; + case Type.DOUBLE_CODE: + method.addInstruction(Opcode.opcx_f2d); + stackHeight += 1; + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + Type.getType(castType)); + // new Type(castType)); + + case Type.DOUBLE_CODE: + switch (castType) { + case Type.BYTE_CODE: + method.addInstruction(Opcode.opcx_d2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2b); + return; + case Type.SHORT_CODE: + method.addInstruction(Opcode.opcx_d2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2s); + return; + case Type.CHARACTER_CODE: + method.addInstruction(Opcode.opcx_d2i); + stackHeight -= 1; + method.addInstruction(Opcode.opcx_i2c); + return; + case Type.INTEGER_CODE: + method.addInstruction(Opcode.opcx_d2i); + stackHeight -= 1; + return; + case Type.LONG_CODE: + method.addInstruction(Opcode.opcx_d2l); + return; + case Type.FLOAT_CODE: + method.addInstruction(Opcode.opcx_d2f); + return; + case Type.DOUBLE_CODE: + return; + } + + throw new IllegalArgumentException("Can't generate cast for type " + + Type.getType(castType)); + // new Type(castType)); + default: + throw new IllegalArgumentException("Can't generate cast from type " + + Type.getType(opType)); + // new Type(castType)); + } + } + + public void visitConstantExpr(final ConstantExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + method.addInstruction(Opcode.opcx_ldc, expr.value()); + stackHeight += expr.type().stackHeight(); + } + + public boolean nowb = false; + + public void visitFieldExpr(final 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; + } + + final CheckExpr c = (CheckExpr) check; + check = c.expr(); + } + + // Do we need to perform the write barrier? + if (!UC && CodeGenerator.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 { + method.addInstruction(Opcode.opcx_putfield, expr.field()); + + } + + stackHeight -= 1; // object + stackHeight -= expr.type().stackHeight(); + + } else { + method.addInstruction(Opcode.opcx_getfield, expr.field()); + stackHeight -= 1; // pop object + stackHeight += expr.type().stackHeight(); + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + method.addInstruction(Opcode.opcx_instanceof, expr.checkType()); + } + + public void visitLocalExpr(final LocalExpr expr) { + + genPostponed(expr); + + final boolean cat2 = expr.type().isWide(); // how many stack positions + // does + // this take up? + + int opcode = -1; // -1 is the flag that it hasn't yet been assigned + // a real value + + if (CodeGenerator.DB_OPT_STACK) { + currentSO.infoDisplay(expr); + } + + if (expr.isDef()) { + + if (!CodeGenerator.OPT_STACK || currentSO.shouldStore(expr)) { + + if (expr.type().isAddress()) { + opcode = Opcode.opcx_astore; + stackHeight -= 1; + } else if (expr.type().isReference()) { + opcode = Opcode.opcx_astore; + stackHeight -= 1; + } else if (expr.type().isIntegral()) { + opcode = Opcode.opcx_istore; + stackHeight -= 1; + } else if (expr.type().equals(Type.LONG)) { + opcode = Opcode.opcx_lstore; + stackHeight -= 2; + } else if (expr.type().equals(Type.FLOAT)) { + opcode = Opcode.opcx_fstore; + stackHeight -= 1; + } else if (expr.type().equals(Type.DOUBLE)) { + opcode = Opcode.opcx_dstore; + stackHeight -= 2; + } else { + throw new IllegalArgumentException( + "Can't generate code for type: " + expr.type() + + " (expr " + expr + ")"); + } + } + } + + else { + + if (CodeGenerator.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 { + opcode = Opcode.opc_swap; + stackHeight -= 1; + } + } + } else { + + if (expr.type().isReference()) { + opcode = Opcode.opcx_aload; + stackHeight += 1; + } else if (expr.type().isIntegral()) { + opcode = Opcode.opcx_iload; + stackHeight += 1; + } else if (expr.type().equals(Type.LONG)) { + opcode = Opcode.opcx_lload; + stackHeight += 2; + } else if (expr.type().equals(Type.FLOAT)) { + opcode = Opcode.opcx_fload; + stackHeight += 1; + } else if (expr.type().equals(Type.DOUBLE)) { + opcode = Opcode.opcx_dload; + stackHeight += 2; + } else { + throw new IllegalArgumentException( + "Can't generate code for type: " + expr.type() + + " (expr " + expr + ")"); + } + } + } + + if (opcode == Opcode.opc_swap) { + method.addInstruction(opcode); // don't give + } else if ((opcode != -1) && !(expr.isDef())) { // if this is a load, we + // want + // the load before any dups. + method.addInstruction(opcode, new LocalVariable(expr.index())); + + if (MethodEditor.OPT_STACK_2) { + method.rememberDef(expr); + } + + } + + if (CodeGenerator.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) + method.addInstruction(Opcode.opc_dup2_x2); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opc_dup_x2); + stackHeight += 1; + } + } + for (int i = 0; i < dup_x1s; i++) { + if (cat2) { + method.addInstruction(Opcode.opc_dup2_x1); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opc_dup_x1); + stackHeight += 1; + } + } + for (int i = 0; i < dups; i++) { + if (cat2) { + method.addInstruction(Opcode.opc_dup2); + stackHeight += 2; + } else { + method.addInstruction(Opcode.opc_dup); + stackHeight += 1; + } + } + } + + // if we have an opcode for a def (i.e., a store), generate it + if ((opcode != -1) && expr.isDef()) { + method.addInstruction(opcode, new LocalVariable(expr.index())); + + if (MethodEditor.OPT_STACK_2) { + method.rememberDef(expr); + } + + } + + if (CodeGenerator.OPT_STACK // if we shouldn't store, + && !currentSO.shouldStore(expr)) { // an extra thing will be + if (cat2) { // on the stack. pop it + method.addInstruction(Opcode.opc_pop2); + stackHeight -= 2; + } else { + method.addInstruction(Opcode.opc_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(final NegExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + if (expr.type().isIntegral()) { + method.addInstruction(Opcode.opcx_ineg); + } else if (expr.type().equals(Type.FLOAT)) { + method.addInstruction(Opcode.opcx_fneg); + } else if (expr.type().equals(Type.LONG)) { + method.addInstruction(Opcode.opcx_lneg); + } else if (expr.type().equals(Type.DOUBLE)) { + method.addInstruction(Opcode.opcx_dneg); + } else { + throw new IllegalArgumentException("Can't generate code for type: " + + expr.type() + " (expr " + expr + ")"); + } + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + method.addInstruction(Opcode.opcx_newarray, expr.elementType()); + } + + public void visitNewExpr(final NewExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + method.addInstruction(Opcode.opcx_new, expr.objectType()); + stackHeight += 1; + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + method.addInstruction(Opcode.opcx_multianewarray, + new MultiArrayOperand(expr.elementType().arrayType( + expr.dimensions().length), expr.dimensions().length)); + stackHeight -= expr.dimensions().length; + stackHeight += 1; + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + genPostponed(expr); + } + + public void visitShiftExpr(final ShiftExpr expr) { + expr.visitChildren(this); + + genPostponed(expr); + + if (expr.type().isIntegral()) { + if (expr.dir() == ShiftExpr.LEFT) { + method.addInstruction(Opcode.opcx_ishl); + stackHeight -= 1; + } else if (expr.dir() == ShiftExpr.RIGHT) { + method.addInstruction(Opcode.opcx_ishr); + stackHeight -= 1; + } else { + method.addInstruction(Opcode.opcx_iushr); + stackHeight -= 1; + } + } else if (expr.type().equals(Type.LONG)) { + if (expr.dir() == ShiftExpr.LEFT) { + method.addInstruction(Opcode.opcx_lshl); + stackHeight -= 1; + } else if (expr.dir() == ShiftExpr.RIGHT) { + method.addInstruction(Opcode.opcx_lshr); + stackHeight -= 1; + } else { + method.addInstruction(Opcode.opcx_lushr); + stackHeight -= 1; + } + } else { + throw new IllegalArgumentException("Can't generate code for type: " + + expr.type() + " (expr " + expr + ")"); + } + } + + public void visitDefExpr(final DefExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + } + + public void visitCatchExpr(final CatchExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + } + + public void visitStackExpr(final StackExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + + if (expr.isDef()) { + method.addInstruction(Opcode.opcx_putstatic, expr.field()); + stackHeight -= expr.type().stackHeight(); + } else { + method.addInstruction(Opcode.opcx_getstatic, expr.field()); + stackHeight += expr.type().stackHeight(); + } + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + expr.visitChildren(this); + genPostponed(expr); + } + + private void genPostponed(final Node node) { + final Instruction inst = (Instruction) postponedInstructions.get(node); + + if (inst != null) { + method.addInstruction(inst); + // Luckily, the rc and aupdate don't change the stack! + postponedInstructions.remove(node); + } + } +} 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..f3a1d4d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/codegen/Liveness.java @@ -0,0 +1,774 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.codegen; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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(final FlowGraph cfg) { + this.cfg = cfg; + computeIntersections(); + } + + /** + * Removes a local expression from the interference graph. + */ + public void removeVar(final 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(final VarExpr isLive, final Block block) { + throw new RuntimeException(); + } + + /** + * Should not be called. + */ + public boolean liveAtEndOfBlock(final VarExpr isLive, final 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(final 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() { + final 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(final VarExpr a, final 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 (Liveness.UNIQUE) { + return true; + } + + final IGNode na = (IGNode) ig.getNode(a); + final 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 HashMap[cfg.size()]; + + Iterator iter = cfg.nodes().iterator(); + + // Initialize nodes and nodeIndices + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + final int blockIndex = cfg.preOrderIndex(block); + nodes[blockIndex] = new ArrayList(); + nodeIndices[blockIndex] = new HashMap(); + } + + // 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(final PhiJoinStmt stmt) { + if (!(stmt.target() instanceof LocalExpr)) { + return; + } + + final 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. + final Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + final int predIndex = cfg.preOrderIndex(pred); + + final List n = nodes[predIndex]; + final Map indices = nodeIndices[predIndex]; + + indices.put(stmt, new Integer(n.size())); + final 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(final PhiCatchStmt stmt) { + } + + public void visitStmt(final 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(final Node node) { + final Node p = parent; + parent = node; + node.visitChildren(this); + parent = p; + } + + public void visitLocalExpr(final 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; + + final List n = nodes[blockIndex]; + final Map indices = nodeIndices[blockIndex]; + + final Integer i = (Integer) indices.get(parent); + + if (i == null) { + if (Liveness.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 (Liveness.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(final PhiCatchStmt stmt) { + NodeInfo info; + + final List n = nodes[blockIndex]; + final Map indices = nodeIndices[blockIndex]; + + final Integer i = (Integer) indices.get(stmt); + + if (i == null) { + if (Liveness.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 (Liveness.DEBUG) { + System.out.println("found " + parent + " at " + i); + } + + info = (NodeInfo) n.get(i.intValue()); + Assert.isTrue(info != null); + } + + final 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(final PhiJoinStmt stmt) { + } + }); + } + + // Iterate over all of the nodes in the IG + final int numDefs = defNodes.size(); + + for (int i = 0; i < numDefs; i++) { + final IGNode node = (IGNode) defNodes.get(i); + final LocalExpr def = node.def; + + // Set of blocks where this variable is live out (i.e. live on + // any of the block's outgoing edges). + final BitSet m = new BitSet(cfg.size()); + + final Iterator uses = def.uses().iterator(); + + // Look at each use of the local variable + while (uses.hasNext()) { + final 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 (Liveness.DEBUG) { + System.out.println("searching for " + def + " from " + + parent); + } + + final Block block = parent.block(); + + if (parent instanceof PhiJoinStmt) { + final 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 + final Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + + if (phi.operandAt(pred) == use) { + final Map indices = nodeIndices[cfg + .preOrderIndex(pred)]; + final 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. + + final Map indices = nodeIndices[cfg.preOrderIndex(block)]; + final 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. + + final int numPhiCatches = phiCatchNodes.size(); + + for (int i = 0; i < numPhiCatches; i++) { + final IGNode node = (IGNode) phiCatchNodes.get(i); + + final PhiCatchStmt phi = (PhiCatchStmt) node.def.parent(); + + final Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + final LocalExpr operand = (LocalExpr) operands.next(); + final LocalExpr def = (LocalExpr) operand.def(); + + if (def != null) { + final IGNode opNode = (IGNode) ig.getNode(def); + + // Conflict with everything the operand conflicts with. + final Iterator edges = new ImmutableIterator(ig + .succs(opNode)); + + while (edges.hasNext()) { + final IGNode otherNode = (IGNode) edges.next(); + + if (otherNode != node) { + if (Liveness.DEBUG) { + System.out.println(otherNode.def + + " conflicts with " + opNode.def + + " and thus with " + node.def); + } + + ig.addEdge(otherNode, node); + ig.addEdge(node, otherNode); + } + } + } + } + } + + if (Liveness.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 phiCatchNodes + * 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(final BitSet m, final List[] nodes, Block block, + int nodeIndex, final IGNode defNode, final Collection phiCatchNodes) { + boolean firstNode = true; + + int blockIndex = cfg.preOrderIndex(block); + + final 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 (Liveness.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. + final ListIterator iter = nodes[blockIndex].listIterator(nodeIndex); + + while (!stop && iter.hasNext()) { + final NodeInfo info = (NodeInfo) iter.next(); + + if (Liveness.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 + final Iterator e = info.defNodes.iterator(); + + while (e.hasNext()) { + final IGNode node = (IGNode) e.next(); + + final 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()) { + final IGNode catchNode = (IGNode) catchPhis.next(); + + final PhiCatchStmt phi = (PhiCatchStmt) catchNode.def + .parent(); + + final Handler handler = (Handler) cfg.handlersMap() + .get(phi.block()); + + Assert.isTrue(handler != null, "Null handler for " + + phi.block()); + + if (handler.protectedBlocks().contains(block)) { + final 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()) { + final LocalExpr expr = (LocalExpr) operands + .next(); + + if (expr.def() == node.def) { + continue PHIS; + } + } + + if (Liveness.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 (Liveness.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 (Liveness.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. + final Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + final int predIndex = cfg.preOrderIndex(pred); + + if (Liveness.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(final 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(final Node node) { + this.node = node; + defNodes = new ArrayList(); + } + } + + class Key { + int blockIndex; + + Node node; + + public Key(final Node node, final int blockIndex) { + this.blockIndex = blockIndex; + this.node = node; + } + + public int hashCode() { + return node.hashCode() ^ blockIndex; + } + + public boolean equals(final Object obj) { + if (obj instanceof Key) { + final 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..0f5b111 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/codegen/Makefile @@ -0,0 +1,24 @@ +# 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. +# +# 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 + +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..b2e3133 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/codegen/RegisterAllocator.java @@ -0,0 +1,657 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.codegen; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 { + 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(RegisterAllocator.MAX_WEIGHT) / Math + .log(RegisterAllocator.LOOP_FACTOR)); + + /** + * 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 RegisterAllocator(final FlowGraph cfg, final Liveness liveness) { + this.cfg = cfg; + this.liveness = liveness; + colorsUsed = 0; + colors = new HashMap(); + + // Construct the interference graph. + final Graph ig = new Graph(); + + Iterator iter = liveness.defs().iterator(); + + while (iter.hasNext()) { + final 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 + final Iterator intersections = liveness.intersections(def); + + while (intersections.hasNext()) { + final 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. + 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(final 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(final 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. + + final IGNode lnode = (IGNode) ig.getNode(stmt.target()); + + final HashSet set = new HashSet(); + + final Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + final 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()) { + final IGNode rnode = (IGNode) ig.getNode(op + .def()); + copies.add(new IGNode[] { lnode, rnode }); + } + } + } + } + } + + public void visitStoreExpr(final StoreExpr expr) { + expr.visitChildren(this); + + if (!(expr.target() instanceof LocalExpr)) { + return; + } + + final 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 + final 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.INTEGER)) { + 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. + + final ArithExpr rhs = (ArithExpr) expr.expr(); + LocalExpr var = null; + + Integer value = null; + + if ((rhs.left() instanceof LocalExpr) + && (rhs.right() instanceof ConstantExpr)) { + + var = (LocalExpr) rhs.left(); + + final 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(); + + final 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)) { + final int incr = value.intValue(); + + if ((short) incr == incr) { + // Only generate an iinc if the increment + // fits in a short + final IGNode rnode = (IGNode) ig.getNode(var.def()); + copies.add(new IGNode[] { lnode, rnode }); + } + } + } + } + + public void visitInitStmt(final InitStmt stmt) { + stmt.visitChildren(this); + + // The initialized variables are precolored. + final 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 HashSet(); + 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()) { + final IGNode[] c = (IGNode[]) iter.next(); + + if ((c[0] == copy[1]) || (c[1] == copy[1])) { + iter.remove(); + } + } + } + } + + // Create a list of uncolored nodes. + final ArrayList uncoloredNodes = new ArrayList(); + + Iterator nodes = ig.nodes().iterator(); + + while (nodes.hasNext()) { + final IGNode node = (IGNode) nodes.next(); + + final 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); + } + } + + // 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(final Object a, final Object b) { + final IGNode na = (IGNode) a; + final 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()) { + final 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 + final BitSet used = new BitSet(); + + final Iterator succs = ig.succs(node).iterator(); + + while (succs.hasNext()) { + final 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()) { + final 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()) { + final LocalExpr def = (LocalExpr) iter.next(); + def.setIndex(node.color); + + final Iterator uses = def.uses().iterator(); + + while (uses.hasNext()) { + final 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 LocalVariable newLocal(final 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? + + final LocalVariable var = new LocalVariable(colorsUsed); + colorsUsed += type.stackHeight(); + 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(final LocalExpr def) { + color = -1; + key = def; + defs = new HashSet(); + defs.add(def); + wide = def.type().isWide(); + 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(final IGNode node) { + Assert.isTrue(wide == node.wide); + + weight += node.weight; + + final Iterator iter = node.defs.iterator(); + + while (iter.hasNext()) { + final 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(final Block block) { + int depth = cfg.loopDepth(block); + + if (depth > RegisterAllocator.MAX_DEPTH) { + return RegisterAllocator.MAX_WEIGHT; + } + + float w = 1.0F; + + while (depth-- > 0) { + w *= RegisterAllocator.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; + + final Iterator iter = defs.iterator(); + + // Look at all(?) of the definitions of the IGNode + while (iter.hasNext()) { + final LocalExpr def = (LocalExpr) iter.next(); + + weight += blockWeight(def.block()); + + final 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()) { + final LocalExpr use = (LocalExpr) uses.next(); + + if (use.parent() instanceof PhiJoinStmt) { + final PhiJoinStmt phi = (PhiJoinStmt) use.parent(); + + final Iterator preds = cfg.preds(phi.block()) + .iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + final 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/.cvsignore b/src/EDU/purdue/cs/bloat/context/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..7c8d867 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/BloatContext.java @@ -0,0 +1,261 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.context; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.inline.*; +import EDU.purdue.cs.bloat.reflect.*; + +/** + * 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 HashSet(); + + protected Set ignoreClasses = new HashSet(); + + protected Set ignoreMethods = new HashSet(); + + protected Set ignoreFields = new HashSet(); + + protected boolean ignoreSystem = false; + + protected CallGraph callGraph; + + protected Set roots; // Root methods of call graph + + protected static void db(final String s) { + if (BloatContext.DEBUG) { + System.out.println(s); + } + } + + protected ClassInfoLoader loader; + + /** + * Constructor. Each BloatContext needs to know about a + * ClassInfoLoader. + */ + public BloatContext(final ClassInfoLoader loader) { + this.loader = loader; + } + + private static ClassLoader systemCL; + static { + final String s = ""; + BloatContext.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(final Type type) { + Class c = null; + try { + c = Class.forName(type.className().replace('/', '.')); + + } catch (final ClassNotFoundException ex) { + System.err.println("** Could not find class " + type.className()); + ex.printStackTrace(System.err); + System.exit(1); + } + + // Have to use == because class loader might be null + return (c.getClassLoader() == BloatContext.systemCL); + } + + public void setRootMethods(final 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(final Type type) { + ignoreClasses.add(type); + } + + public void addIgnoreMethod(final MemberRef method) { + ignoreMethods.add(method); + } + + public void addIgnoreField(final MemberRef field) { + ignoreFields.add(field); + } + + public void setIgnoreSystem(final boolean ignore) { + this.ignoreSystem = ignore; + } + + public boolean ignoreClass(final 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 (ignoreClasses.contains(type)) { + return (true); + + } else if (type.isPrimitive()) { + addIgnoreClass(type); + return (true); + + } else { + if (this.ignoreSystem) { + if (BloatContext.isSystem(type)) { + addIgnoreClass(type); + return (true); + } + } + + String packageName = type.className(); + final 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. + final Iterator packages = ignorePackages.iterator(); + while (packages.hasNext()) { + final String s = (String) packages.next(); + if (type.className().startsWith(s)) { + addIgnoreClass(type); + return (true); + } + } + + return (false); + } + } + + public boolean ignoreMethod(final MemberRef method) { + if (ignoreMethods.contains(method)) { + return (true); + + } else if (ignoreClass(method.declaringClass())) { + addIgnoreMethod(method); + return (true); + } + return (false); + } + + public boolean ignoreField(final 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(final String[] args) { + final PrintWriter out = new PrintWriter(System.out, true); + final PrintWriter err = new PrintWriter(System.err, true); + + final BloatContext context = new CachingBloatContext( + new ClassFileLoader(), new ArrayList(), false); + + final 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]); + final String type = args[i].replace('.', '/'); + context.addIgnoreClass(Type.getType("L" + type + ";")); + + } else { + // A type + final String type = args[i].replace('.', '/'); + types.add(Type.getType("L" + type + ";")); + } + } + + out.println(""); + + final Iterator iter = types.iterator(); + while (iter.hasNext()) { + final Type type = (Type) iter.next(); + out.println("Ignore " + type + "? " + context.ignoreClass(type)); + } + } + +} diff --git a/src/EDU/purdue/cs/bloat/context/BloatingClassLoader.java b/src/EDU/purdue/cs/bloat/context/BloatingClassLoader.java new file mode 100644 index 0000000..a18c64f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/BloatingClassLoader.java @@ -0,0 +1,147 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.context; + +import java.io.*; +import java.net.*; +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * BloatingClassLoader is a Java class loader that BLOATs a class + * before it is loader into a Java Virtual Machine. It loads its classes from a + * set of {@link URL}s. + */ +public abstract class BloatingClassLoader extends URLClassLoader { + + /** + * A ClassInfoLoader that loads classes from the same locations as this + * class loader. + */ + ClassInfoLoader loader = new BloatingClassInfoLoader(); + + /** The context that is used to edit classes, etc. */ + private final EditorContext context = new PersistentBloatContext(loader, + false); + + /** + * Maps ClassInfos to their committed bytes (as a ByteArrayOutputStream) + */ + private final Map classBytes = new HashMap(); + + // //////////////////// Constructors ///////////////////////// + + /** + * Creates a new BloatingClassLoader that loads its classes + * from a given set of URLs. + */ + public BloatingClassLoader(final URL[] urls) { + super(urls); + } + + /** + * Creates a new BloatingClassLoader that loads its classes + * from a given set of URLs. Before attempting to load a class, this + * BloatingClassLoader will delegate to its parent class + * loader. + */ + public BloatingClassLoader(final URL[] urls, final ClassLoader parent) { + super(urls); + } + + /** + * Before the Class is created, invoke {@link + * #bloat(ClassEditor)}. + */ + protected Class findClass(final String name) throws ClassNotFoundException { + + final ClassInfo info = this.loader.loadClass(name); + final ClassEditor ce = this.context.editClass(info); + + this.bloat(ce); + + ce.commit(); + final ByteArrayOutputStream baos = (ByteArrayOutputStream) this.classBytes + .get(info); + Assert.isNotNull(baos, "No bytes for " + name); + + final byte[] bytes = baos.toByteArray(); + return super.defineClass(name, bytes, 0, bytes.length); + } + + /** + * Returns a ClassInfoLoader that loads classes from the same + * place as this ClassLoader. + */ + public ClassInfoLoader getClassInfoLoader() { + return this.loader; + } + + /** + * This method is invoked as a class is being loaded. + */ + protected abstract void bloat(ClassEditor ce); + + /** + * This inner class is a ClassInfoLoader that loads classes from the same + * locations as the outer BloatClassLoader. The primary reason that we have + * this class is because the loadClass method of ClassInfoLoader has a + * different signature from ClassLoader. Hence, a ClassLoader cannot be a + * ClassInfoLoader. + */ + class BloatingClassInfoLoader implements ClassInfoLoader { + + public ClassInfo loadClass(final String name) + throws ClassNotFoundException { + + final String classFileName = name.replace('.', '/') + ".class"; + final InputStream is = BloatingClassLoader.this + .getResourceAsStream(classFileName); + if (is == null) { + throw new ClassNotFoundException("Could not find class " + name); + } + + final DataInputStream dis = new DataInputStream(is); + return new ClassFile(null, this, dis); + } + + public ClassInfo newClass(final int modifiers, final int classIndex, + final int superClassIndex, final int[] interfaceIndexes, + final java.util.List constants) { + + return new ClassFile(modifiers, classIndex, superClassIndex, + interfaceIndexes, constants, this); + } + + public OutputStream outputStreamFor(final ClassInfo info) + throws IOException { + + // Maintain a mapping between ClassInfos and their committed bytes + final OutputStream os = new ByteArrayOutputStream(); + classBytes.put(info, os); + return (os); + } + } +} diff --git a/src/EDU/purdue/cs/bloat/context/CachingBloatContext.java b/src/EDU/purdue/cs/bloat/context/CachingBloatContext.java new file mode 100644 index 0000000..505b70d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/CachingBloatContext.java @@ -0,0 +1,461 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.context; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Does a lot of the same stuff as PersistentBloatContext except that + * it manages the chaches of BLOAT objects. For example, when a + * MethodEditor is no longer needed, it is removed from the cache if + * it is not dirty. This context is meant to used in volatile memory. + */ +public class CachingBloatContext extends PersistentBloatContext { + + // Keep track of reference counts in a manner reminiscent of the old + // Editor class. + protected Map classRC; + + protected Map methodRC; + + protected Map fieldRC; + + /** + * Constructor. + * + * @param loader + * Used to load classes + * @param classes + * Some initial classes in the context + * @param closure + * Do we look for the maximum number of classes? + */ + public CachingBloatContext(final ClassInfoLoader loader, + final Collection classes, final boolean closure) { + super(loader, closure); + + classRC = new HashMap(); + methodRC = new HashMap(); + fieldRC = new HashMap(); + + addClasses(classes); + } + + public ClassEditor newClass(final int modifiers, final String className, + final Type superType, final Type[] interfaces) { + + final ClassEditor ce = super.newClass(modifiers, className, superType, + interfaces); + final ClassInfo info = ce.classInfo(); + classRC.put(info, new Integer(1)); + + return ce; + } + + public ClassEditor editClass(final ClassInfo info) { + // Check the cache + ClassEditor ce = (ClassEditor) classEditors.get(info); + + if (ce == null) { + ce = new ClassEditor(this, info); + classEditors.put(info, ce); + classRC.put(info, new Integer(1)); + + if (!classInfos.containsValue(info)) { + final String className = ce.name().intern(); + BloatContext.db("editClass(ClassInfo): " + className + " -> " + + info); + classInfos.put(className, info); + } + + } else { + final Integer rc = (Integer) classRC.get(info); + classRC.put(info, new Integer(rc.intValue() + 1)); + } + + return (ce); + } + + public MethodEditor editMethod(final MemberRef method) + throws NoSuchMethodException { + + // Check the MethodInfo cache + final MethodInfo info = (MethodInfo) methodInfos.get(method); + + if (info == null) { + // Groan, we have to do this the HARD way. + BloatContext.db("Creating a new MethodEditor for " + method); + final NameAndType nat = method.nameAndType(); + final String name = nat.name(); + final Type type = nat.type(); + + try { + final ClassEditor ce = editClass(method.declaringClass()); + final MethodInfo[] methods = ce.methods(); + + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = editMethod(methods[i]); + + if (me.name().equals(name) && me.type().equals(type)) { + // The call to editMethod should have already handled + // the + // methodEditors mapping, but we still need to do + // methodInfos. + methodInfos.put(method, methods[i]); + release(ce.classInfo()); + return (me); + } + } + + release(ce.classInfo()); + + } catch (final ClassNotFoundException ex1) { + throw new NoSuchMethodException(method.toString() + "(" + + ex1.getMessage() + ")"); + + } catch (final ClassFormatException ex2) { + throw new NoSuchMethodException(method.toString() + "(" + + ex2.getMessage() + ")"); + + } + + throw new NoSuchMethodException(method.toString()); + } + + return (editMethod(info)); + } + + public MethodEditor editMethod(final MethodInfo info) { + // Check methodEditors cache + MethodEditor me = (MethodEditor) methodEditors.get(info); + + if (me == null) { + final ClassInfo classInfo = info.declaringClass(); + me = new MethodEditor(editClass(classInfo), info); + release(classInfo); + + methodEditors.put(info, me); + methodRC.put(info, new Integer(1)); + BloatContext + .db("Creating a new MethodEditor for " + me.memberRef()); + + } else { + final Integer rc = (Integer) methodRC.get(info); + methodRC.put(info, new Integer(rc.intValue() + 1)); + } + + return (me); + } + + public FieldEditor editField(final MemberRef field) + throws NoSuchFieldException { + + // Just like we had to do with methods + final FieldInfo info = (FieldInfo) fieldInfos.get(field); + + if (info == null) { + final NameAndType nat = field.nameAndType(); + final String name = nat.name(); + final Type type = nat.type(); + + try { + final ClassEditor ce = editClass(field.declaringClass()); + final FieldInfo[] fields = ce.fields(); + + for (int i = 0; i < fields.length; i++) { + final FieldEditor fe = editField(fields[i]); + + if (fe.name().equals(name) && fe.type().equals(type)) { + fieldInfos.put(field, fields[i]); + release(ce.classInfo()); + return (fe); + } + + release(fields[i]); + } + + release(ce.classInfo()); + } catch (final ClassNotFoundException ex1) { + } catch (final ClassFormatException ex2) { + } + + throw new NoSuchFieldException(field.toString()); + } + + return (editField(info)); + } + + public FieldEditor editField(final FieldInfo info) { + // Check the cache + FieldEditor fe = (FieldEditor) fieldEditors.get(info); + + BloatContext.db("Editing " + info); + + if (fe == null) { + final ClassInfo classInfo = info.declaringClass(); + fe = new FieldEditor(editClass(classInfo), info); + release(classInfo); + + fieldEditors.put(info, fe); + fieldRC.put(info, new Integer(0)); + BloatContext.db("Creating a new FieldEditor for " + + fe.nameAndType()); + + } else { + final Integer rc = (Integer) fieldRC.get(info); + fieldRC.put(info, new Integer(rc.intValue() + 1)); + + } + + return (fe); + } + + public void release(final ClassInfo info) { + final Integer rc = (Integer) classRC.get(info); + + if ((rc != null) && (rc.intValue() > 1)) { + // Not done yet; + classRC.put(info, new Integer(rc.intValue() - 1)); + return; + + } + + ClassEditor ce = (ClassEditor) classEditors.get(info); + if ((ce != null) && ce.isDirty()) { + return; + } + + // We're done with this class, remove all traces of it + ce = (ClassEditor) classEditors.remove(info); + classRC.remove(info); + classEditors.remove(info); + + final Iterator iter = classInfos.keySet().iterator(); + while (iter.hasNext()) { + final String name = (String) iter.next(); + final ClassInfo info2 = (ClassInfo) classInfos.get(name); + if (info2 == info) { + BloatContext.db("Removing ClassInfo: " + name + " -> " + info2); + classInfos.remove(name); + break; + } + } + + if (ce != null) { + // Remove all of the class's fields and methods also + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + release(methods[i]); + } + + final FieldInfo[] fields = ce.fields(); + for (int i = 0; i < fields.length; i++) { + release(fields[i]); + } + } + + } + + public void release(final MethodInfo info) { + final Integer rc = (Integer) classRC.get(info); + + if ((rc != null) && (rc.intValue() > 1)) { + methodRC.put(info, new Integer(rc.intValue() - 1)); + return; + } + + final MethodEditor me = (MethodEditor) methodEditors.get(info); + + // We should keep dirty methods around. My original thought was + // that if we committed dirty methods when they were released, we + // risk having MethodEditors editing different versions of the + // same method. So, if we don't release dirty methods, we'll only + // have ONE MethodEditor. + if ((me != null) && me.isDirty()) { + return; + } + + // We're done with this method, remove all traces of it + methodRC.remove(info); + methodEditors.remove(info); + + final Iterator iter = methodInfos.keySet().iterator(); + while (iter.hasNext()) { + final MemberRef ref = (MemberRef) iter.next(); + final MethodInfo info2 = (MethodInfo) methodInfos.get(ref); + if (info2 == info) { + methodInfos.remove(ref); + break; + } + } + } + + public void release(final FieldInfo info) { + final Integer rc = (Integer) fieldRC.get(info); + + BloatContext.db("Releasing " + info); + + if ((rc != null) && (rc.intValue() > 1)) { + fieldRC.put(info, new Integer(rc.intValue() - 1)); + return; + } + + final FieldEditor fe = (FieldEditor) fieldEditors.get(info); + if ((fe != null) && fe.isDirty()) { + return; + } + + // We're done with this field, remove all traces of it + fieldRC.remove(info); + fieldEditors.remove(info); + + final Iterator iter = fieldInfos.keySet().iterator(); + while (iter.hasNext()) { + final MemberRef ref = (MemberRef) iter.next(); + final FieldInfo info2 = (FieldInfo) fieldInfos.get(ref); + if (info2 == info) { + fieldInfos.remove(ref); + break; + } + } + } + + public void commit(final ClassInfo info) { + super.commit(info); + + classEditors.remove(info); + classRC.remove(info); + } + + public void commit(final MethodInfo info) { + super.commit(info); + + methodEditors.remove(info); + methodRC.remove(info); + } + + public void commit(final FieldInfo info) { + super.commit(info); + + fieldEditors.remove(info); + fieldRC.remove(info); + } + + public void commit() { + Iterator iter = fieldEditors.values().iterator(); + while (iter.hasNext()) { + final FieldEditor fe = (FieldEditor) iter.next(); + commit(fe.fieldInfo()); + } + + iter = methodEditors.values().iterator(); + while (iter.hasNext()) { + final MethodEditor me = (MethodEditor) iter.next(); + commit(me.methodInfo()); + } + + iter = classEditors.values().iterator(); + while (iter.hasNext()) { + final ClassEditor ce = (ClassEditor) iter.next(); + commit(ce.classInfo()); + } + } + + /** + * Return a textual description of all of the caches. Useful if we run out + * of memory. + */ + public String toString() { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw, true); + + pw.println("Context of caches in CachingBloatContext..."); + + pw.println(" Class Infos"); + Iterator iter = classInfos.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + classInfos.get(key)); + } + + pw.println(" Class Editors"); + iter = classEditors.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + classEditors.get(key)); + } + + pw.println(" Class RC"); + iter = classRC.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + classRC.get(key)); + } + + pw.println(" Method Infos"); + iter = methodInfos.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + methodInfos.get(key)); + } + + pw.println(" Method Editors"); + iter = methodEditors.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + methodEditors.get(key)); + } + + pw.println(" Method RC"); + iter = methodRC.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + methodRC.get(key)); + } + + pw.println(" Field Infos"); + iter = fieldInfos.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + fieldInfos.get(key)); + } + + pw.println(" Field Editors"); + iter = fieldEditors.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + fieldEditors.get(key)); + } + + pw.println(" Field RC"); + iter = fieldRC.keySet().iterator(); + while (iter.hasNext()) { + final Object key = iter.next(); + pw.println(" " + key + " -> " + fieldRC.get(key)); + } + + return (sw.toString()); + } +} diff --git a/src/EDU/purdue/cs/bloat/context/Makefile b/src/EDU/purdue/cs/bloat/context/Makefile new file mode 100644 index 0000000..dbdfa89 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/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. +# +# 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 + + +CLASS = \ + BloatContext.class\ + CachingBloatContext.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..636933f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/context/PersistentBloatContext.java @@ -0,0 +1,438 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.context; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.reflect.*; + +/** + * 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 classInfos; // Maps Strings to ClassInfos + + protected Map methodInfos; // Maps MemberRefs to MethodInfos + + protected Map fieldInfos; // Maps MemberRefs to FieldInfos + + protected Map classEditors; // Maps ClassInfos to ClassEditors + + protected Map methodEditors; // Maps MethodInfos to MethodEditors + + protected Map fieldEditors; // Maps MethodInfos to FieldEditors + + public static boolean DB_COMMIT = false; + + protected static void comm(final String s) { + if (PersistentBloatContext.DB_COMMIT || BloatContext.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(final ClassInfoLoader 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? + */ + protected PersistentBloatContext(final ClassInfoLoader loader, + final boolean closure) { + super(loader); + BloatContext.db("Creating a new BloatContext"); + + // Create a bunch of the mappings we maintain. Make sure to do + // this before anything else! + classInfos = new HashMap(); + methodInfos = new HashMap(); + fieldInfos = new HashMap(); + + classEditors = new HashMap(); + methodEditors = new HashMap(); + fieldEditors = new HashMap(); + + // 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(final Collection classes) { + final Iterator iter = classes.iterator(); + while (iter.hasNext()) { + final String className = (String) iter.next(); + this.hierarchy.addClassNamed(className); + } + } + + public ClassInfo 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('.', '/').intern(); + + // Check the cache of ClassInfos + ClassInfo info = (ClassInfo) classInfos.get(className); + + if (info == null) { + BloatContext.db("BloatContext: Loading class " + className); + info = loader.loadClass(className); + hierarchy.addClassNamed(className); + BloatContext.db("loadClass: " + className + " -> " + info); + classInfos.put(className, info); + } + + return (info); + } + + public ClassInfo newClassInfo(final int modifiers, final int classIndex, + final int superClassIndex, final int[] interfaceIndexes, + final List constants) { + + return this.loader.newClass(modifiers, classIndex, superClassIndex, + interfaceIndexes, constants); + } + + public ClassHierarchy getHierarchy() { + return (this.hierarchy); + } + + public ClassEditor newClass(final int modifiers, String className, + final Type superType, final Type[] interfaces) { + + final ClassEditor ce = new ClassEditor(this, modifiers, className, + superType, interfaces); + final ClassInfo info = ce.classInfo(); + + className = ce.name().intern(); + + BloatContext.db("editClass(ClassInfo): " + className + " -> " + info); + + classInfos.put(className, info); + classEditors.put(info, ce); + + return ce; + } + + public ClassEditor 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(); + + ClassInfo info = (ClassInfo) classInfos.get(className); + + if (info == null) { + info = loadClass(className); + // db("editClass(String): " + className + " -> " + info); + // classInfos.put(className, info); + } + + return (editClass(info)); + } + + public ClassEditor editClass(final Type classType) + throws ClassNotFoundException, ClassFormatException { + return (editClass(classType.className())); + } + + public ClassEditor editClass(final ClassInfo info) { + // Check the cache + ClassEditor ce = (ClassEditor) classEditors.get(info); + + if (ce == null) { + ce = new ClassEditor(this, info); + classEditors.put(info, ce); + + if (!classInfos.containsValue(info)) { + final String className = ce.name().intern(); + BloatContext.db("editClass(ClassInfo): " + className + " -> " + + info); + classInfos.put(className, info); + } + } + + return (ce); + } + + public MethodEditor editMethod(final MemberRef method) + throws NoSuchMethodException { + + // Check the MethodInfo cache + final MethodInfo info = (MethodInfo) methodInfos.get(method); + + if (info == null) { + // Groan, we have to do this the HARD way. + BloatContext.db("Creating a new MethodEditor for " + method); + final NameAndType nat = method.nameAndType(); + final String name = nat.name(); + final Type type = nat.type(); + + try { + final ClassEditor ce = editClass(method.declaringClass()); + final MethodInfo[] methods = ce.methods(); + + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = editMethod(methods[i]); + + if (me.name().equals(name) && me.type().equals(type)) { + // The call to editMethod should have already handled + // the + // methodEditors mapping, but we still need to do + // methodInfos. + methodInfos.put(method, methods[i]); + release(ce.classInfo()); + return (me); + } + + release(methods[i]); + } + + } catch (final ClassNotFoundException ex1) { + } catch (final ClassFormatException ex2) { + } + + throw new NoSuchMethodException(method.toString()); + } + + return (editMethod(info)); + } + + public MethodEditor editMethod(final MethodInfo info) { + // Check methodEditors cache + MethodEditor me = (MethodEditor) methodEditors.get(info); + + if (me == null) { + me = new MethodEditor(editClass(info.declaringClass()), info); + methodEditors.put(info, me); + BloatContext + .db("Creating a new MethodEditor for " + me.memberRef()); + } + + return (me); + } + + public FieldEditor editField(final MemberRef field) + throws NoSuchFieldException { + + // Just like we had to do with methods + final FieldInfo info = (FieldInfo) fieldInfos.get(field); + + if (info == null) { + final NameAndType nat = field.nameAndType(); + final String name = nat.name(); + final Type type = nat.type(); + + try { + final ClassEditor ce = editClass(field.declaringClass()); + final FieldInfo[] fields = ce.fields(); + + for (int i = 0; i < fields.length; i++) { + final FieldEditor fe = editField(fields[i]); + + if (fe.name().equals(name) && fe.type().equals(type)) { + fieldInfos.put(field, fields[i]); + release(ce.classInfo()); + return (fe); + } + + release(fields[i]); + } + } catch (final ClassNotFoundException ex1) { + } catch (final ClassFormatException ex2) { + } + + throw new NoSuchFieldException(field.toString()); + } + + return (editField(info)); + } + + public FieldEditor editField(final FieldInfo info) { + // Check the cache + FieldEditor fe = (FieldEditor) fieldEditors.get(info); + + if (fe == null) { + fe = new FieldEditor(editClass(info.declaringClass()), info); + fieldEditors.put(info, fe); + BloatContext.db("Creating a new FieldEditor for " + + fe.nameAndType()); + } + + return (fe); + } + + public void release(final ClassInfo info) { + // Since we keep around all data, do nothing + } + + public void release(final ClassEditor ce) { + // Since we keep around all data, do nothing + } + + public void release(final MethodInfo info) { + // Since we keep around all data, do nothing + } + + public void release(final FieldInfo info) { + // Since we keep around all data, do nothing + } + + /** + * Classes that are ignored are not committed. + * + * @see #ignoreClass(Type) + */ + public void commit(final ClassInfo info) { + final Type type = Type.getType("L" + info.name() + ";"); + if (ignoreClass(type)) { + return; + } + + final ClassEditor ce = editClass(info); + + // Commit all of the class's methods and fields + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + commit(methods[i]); + } + + final FieldInfo[] fields = ce.fields(); + for (int i = 0; i < fields.length; i++) { + commit(fields[i]); + } + + ce.commit(); + + ce.setDirty(false); + release(info); + } + + public void commit(final MethodInfo info) { + final MethodEditor me = editMethod(info); + me.commit(); + + // We make the method's class dirty so it, too, will be committed + me.declaringClass().setDirty(true); + me.setDirty(false); + release(info); + } + + public void commit(final FieldInfo info) { + final FieldEditor fe = editField(info); + fe.commit(); + + // We make the method's class dirty so it, too, will be committed + fe.declaringClass().setDirty(true); + fe.setDirty(false); + release(info); + } + + public void commit() { + Object[] array = fieldEditors.values().toArray(); + for (int i = 0; i < array.length; i++) { + final FieldEditor fe = (FieldEditor) array[i]; + if (!ignoreField(fe.memberRef())) { + commit(fe.fieldInfo()); + } + } + + array = methodEditors.values().toArray(); + for (int i = 0; i < array.length; i++) { + final MethodEditor me = (MethodEditor) array[i]; + if (!ignoreMethod(me.memberRef())) { + commit(me.methodInfo()); + } + } + + array = classEditors.values().toArray(); + for (int i = 0; i < array.length; i++) { + final ClassEditor ce = (ClassEditor) array[i]; + if (!ignoreClass(ce.type())) { + commit(ce.classInfo()); + } + } + } + + public void commitDirty() { + PersistentBloatContext.comm("Committing dirty data"); + + // Commit all dirty fields + Object[] array = this.fieldEditors.values().toArray(); + for (int i = 0; i < array.length; i++) { + final FieldEditor fe = (FieldEditor) array[i]; + if (fe.isDirty() && !ignoreField(fe.memberRef())) { + PersistentBloatContext.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++) { + final MethodEditor me = (MethodEditor) array[i]; + if (me.isDirty() && !ignoreMethod(me.memberRef())) { + PersistentBloatContext.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++) { + final ClassEditor ce = (ClassEditor) array[i]; + if (ce.isDirty() && !ignoreClass(ce.type())) { + PersistentBloatContext.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/decorate/.cvsignore b/src/EDU/purdue/cs/bloat/decorate/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/decorate/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/decorate/Main.java b/src/EDU/purdue/cs/bloat/decorate/Main.java new file mode 100644 index 0000000..df727a8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/decorate/Main.java @@ -0,0 +1,727 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.decorate; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.trans.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Inserts residency, update, or swizzle checks into the methods of the classes + * specified on the command line. + * + * Usage: java EDU.purdue.cs.bloat.decorate.Main [-options] classes output_dir + * + * where options include: -help print out this message -v -verbose turn on + * verbose mode (can be given multiple times) -classpath skip the given class or package (this option can be given + * more than once) -only skip all but the given class or + * package (this option can be given more than once) -rc insert residency checks + * (default) -norc don't insert residency checks -uc insert update checks + * (default) -sc insert array swizzle checks (default) -nosc don't insert array + * swizzle checkso + * + */ +public class Main implements Opcode { + private static int VERBOSE = 0; // The level of verbosity + + private static boolean FORCE = false; + + private static boolean CLOSURE = false; + + private static boolean RC = true; // Insert residency checks? + + private static boolean UC = true; // Insert update checks? + + private static boolean SC = true; // Insert swizzle checks? + + private static final List SKIP = new ArrayList(); + + private static final List ONLY = new ArrayList(); + + private static final int NONE = 0; + + private static final int POINTER = 1; + + private static final int SCALAR = 2; + + /** + * Parse the command line. Inserts residency, update, and swizzle checks + * into the bytecode of the methods of the specified classes. + */ + public static void main(final String[] args) { + final ClassFileLoader loader = new ClassFileLoader(); + List classes = new ArrayList(); // Names of classes from command line + boolean gotdir = false; // Did user specify an output dir? + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + Main.VERBOSE++; + } else if (args[i].equals("-help")) { + Main.usage(); + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.setClassPath(classpath); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.SKIP.add(pkg); + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.ONLY.add(pkg); + } else if (args[i].equals("-closure")) { + Main.CLOSURE = true; + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + } else if (args[i].equals("-f")) { + Main.FORCE = true; + } else if (args[i].equals("-norc")) { + Main.RC = false; + } else if (args[i].equals("-rc")) { + Main.RC = true; + } else if (args[i].equals("-nouc")) { + Main.UC = false; + } else if (args[i].equals("-uc")) { + Main.UC = true; + } else if (args[i].equals("-nosc")) { + Main.SC = false; + } else if (args[i].equals("-sc")) { + Main.SC = true; + } else if (args[i].startsWith("-")) { + Main.usage(); + } else if (i == args.length - 1) { + // Last argument is the name of the outpu directory + final File f = new File(args[i]); + + if (f.exists() && !f.isDirectory()) { + System.err.println("No such directory: " + f.getPath()); + System.exit(2); + } + + loader.setOutputDir(f); + gotdir = true; + } else { + classes.add(args[i]); + } + } + + if (!gotdir) { + Main.usage(); + } + + if (classes.size() == 0) { + Main.usage(); + } + + if (Main.VERBOSE > 3) { + ClassFileLoader.DEBUG = true; + ClassEditor.DEBUG = true; + } + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + // Load each class specified on the command line + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + loader.loadClass(name); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + final BloatContext context = new CachingBloatContext(loader, classes, + Main.CLOSURE); + + if (!Main.CLOSURE) { + final Iterator e = classes.iterator(); + + while (e.hasNext()) { + final String name = (String) e.next(); + try { + final ClassInfo info = loader.loadClass(name); + Main.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } else { + classes = null; + + final ClassHierarchy hier = context.getHierarchy(); + + final Iterator e = hier.classes().iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + try { + final ClassInfo info = loader.loadClass(t.className()); + Main.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } + } + } + + private static void usage() { + System.err + .println("Usage: java EDU.purdue.cs.bloat.decorate.Main " + + "\n [-options] classes output_dir" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -v -verbose turn on verbose mode " + + "(can be given multiple times)" + + "\n -classpath " + + "\n list directories in which to look for classes" + + "\n -f decorate files even if up-to-date" + + "\n -closure recursively decorate 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 (this option can be given more than once)" + + "\n -only " + + "\n skip all but the given class or package" + + "\n (this option can be given more than once)" + + "\n -rc insert residency checks (default)" + + "\n -norc don't insert residency checks" + + "\n -uc insert update checks (default)" + + "\n -sc insert array swizzle checks (default)" + + "\n -nosc don't insert array swizzle checks"); + System.exit(0); + } + + /** + * Adds residency/update/swizzle checks to all of the methods in a given + * class. + * + * @param context + * Information about all the classes we're dealing with + * @param info + * Information about the class we're decorating + */ + private static void decorateClass(final EditorContext context, + final ClassInfo info) { + final ClassFile classFile = (ClassFile) info; + + // Check to see if the class file is up-to-date + if (!Main.FORCE) { + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (Main.VERBOSE > 1) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (Main.VERBOSE > 2) { + classFile.print(System.out); + } + + final ClassEditor c = context.editClass(info); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (Main.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < Main.ONLY.size(); i++) { + final String pkg = (String) Main.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 < Main.SKIP.size(); i++) { + final String pkg = (String) Main.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (Main.VERBOSE > 0) { + System.out.println("Skipping " + c.type().className()); + } + + context.release(info); + return; + } + + if (Main.VERBOSE > 0) { + System.out.println("Decorating class " + c.type().className()); + } + + if (Main.VERBOSE > 2) { + ((ClassFile) info).print(System.out); + } + + final MethodInfo[] methods = c.methods(); + + // Add residency checks (via transform()) to each method in the class + for (int j = 0; j < methods.length; j++) { + MethodEditor m; + + try { + m = context.editMethod(methods[j]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + Main.transform(m); + context.commit(methods[j]); + } + + context.commit(info); + } + + /** + * Inserts residency/update/swizzle checks into a method. Iterates over the + * bytecodes in the method and inserts the appropriate residency opcode. + * + * @param method + * The method to which to add checks. + * + * @see MethodEditor#code + */ + private static void transform(final MethodEditor method) { + if (Main.VERBOSE > 1) { + System.out.println("Decorating method " + method); + } + + // Optimize initialization of arrays to speed things up. + CompactArrayInitializer.transform(method); + + final ListIterator iter = method.code().listIterator(); + + // Go through the code (Instructions and Labels) in the method + INST: while (iter.hasNext()) { + final Object ce = iter.next(); + + if (Main.VERBOSE > 2) { + System.out.println("Examining " + ce); + } + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + int uctype = Main.NONE; // Type of update check (POINTER or + // SCALAR) + boolean insert_sc = false; // Insert swizzle check (aaload + // only)? + + final int opc = inst.opcodeClass(); + int depth; + + switch (opc) { + case opcx_arraylength: + case opcx_athrow: + case opcx_getfield: + case opcx_instanceof: { + depth = 0; + break; + } + case opcx_iaload: + case opcx_laload: + case opcx_faload: + case opcx_daload: + case opcx_baload: + case opcx_caload: + case opcx_saload: { + depth = 1; + break; + } + case opcx_aaload: { + depth = 1; + insert_sc = true; + break; + } + case opcx_iastore: + case opcx_fastore: + case opcx_aastore: + case opcx_bastore: + case opcx_castore: + case opcx_sastore: { + depth = 2; + break; + } + case opcx_lastore: + case opcx_dastore: { + depth = 3; + break; + } + case opcx_putfield: { + final MemberRef ref = (MemberRef) inst.operand(); + depth = ref.type().stackHeight(); + if (ref.type().isReference()) { + uctype = Main.POINTER; + } else { + uctype = Main.SCALAR; + } + break; + } + case opcx_invokevirtual: + case opcx_invokespecial: + case opcx_invokeinterface: { + final MemberRef ref = (MemberRef) inst.operand(); + depth = ref.type().stackHeight(); + break; + } + case opcx_rc: { + // Skip any existing residency checks. + iter.remove(); + continue INST; + } + case opcx_aupdate: { + // Skip any existing update checks. + iter.remove(); + continue INST; + } + case opcx_supdate: { + // Skip any existing update checks. + iter.remove(); + continue INST; + } + default: { + continue INST; + } + } + + Instruction addInst; + + // Insert a residency check... + if (Main.RC) { + Object t; + + // ////////////////////////////////// + // Before... + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + // + // After... + // +-----+----+------+-----------+ + // | ... | RC | inst | afterInst | + // +-----+----+------+-----------+ + // ^prev ^next + // ////////////////////////////////// + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + addInst = new Instruction(Opcode.opcx_rc, + new Integer(depth)); + iter.add(addInst); + + // +-----+----+------+-----------+ + // | ... | RC | inst | afterInst | + // +-----+----+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+----+------+-----------+ + // | ... | RC | inst | afterInst | + // +-----+----+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+----+------+-----------+ + // | ... | RC | inst | afterInst | + // +-----+----+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+----+------+-----------+ + // | ... | RC | inst | afterInst | + // +-----+----+------+-----------+ + // ^prev ^next + + if (Main.VERBOSE > 2) { + System.out.println("Inserting " + addInst + " before " + + inst); + } + } else { + if (Main.VERBOSE > 2) { + System.out.println("Not inserting rc before " + inst); + } + } + + // Insert a swizzle check... + if (insert_sc) { + if (Main.SC) { + Object t; + + // //////////////////////////////////////////// + // Before... + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + // + // After... + // +-----+------+----------+------+-----------+ + // | ... | dup2 | aswizzle | inst | afterInst | + // +-----+------+----------+------+-----------+ + // ^prev ^next + // ///////////////////////////////////////////// + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + addInst = new Instruction(Opcode.opcx_dup2); + iter.add(addInst); + + // +-----+------+------+-----------+ + // | ... | dup2 | inst | afterInst | + // +-----+------+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+------+------+-----------+ + // | ... | dup2 | inst | afterInst | + // +-----+------+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+------+------+-----------+ + // | ... | dup2 | inst | afterInst | + // +-----+------+------+-----------+ + // ^prev ^next + + addInst = new Instruction(Opcode.opcx_aswizzle); + iter.add(addInst); + + // +-----+------+----------+------+-----------+ + // | ... | dup2 | aswizzle | inst | afterInst | + // +-----+------+----------+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+------+----------+------+-----------+ + // | ... | dup2 | aswizzle | inst | afterInst | + // +-----+------+----------+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+------+----------+------+-----------+ + // | ... | dup2 | aswizzle | inst | afterInst | + // +-----+------+----------+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+------+----------+------+-----------+ + // | ... | dup2 | aswizzle | inst | afterInst | + // +-----+------+----------+------+-----------+ + // ^prev ^next + + if (Main.VERBOSE > 2) { + System.out + .println("Inserting dup2,aswizzle before " + + inst); + } + } + + else { + if (Main.VERBOSE > 2) { + System.out.println("Not inserting aswizzle before " + + inst); + } + } + } + + // Insert an update check... + if (uctype != Main.NONE) { + if (Main.UC) { + Object t; + + // //////////////////////////////////////////// + // Before... + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + // + // After... + // +-----+---------+------+-----------+ + // | ... | aupdate | inst | afterInst | + // +-----+---------+------+-----------+ + // ^prev ^next + // ///////////////////////////////////////////// + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+------+-----------+ + // | ... | inst | afterInst | + // +-----+------+-----------+ + // ^prev ^next + + addInst = new Instruction(Opcode.opcx_aupdate, + new Integer(depth)); + /* + * if (uctype == POINTER) { addInst = new + * Instruction(opcx_aupdate, new Integer(depth)); } else { + * addInst = new Instruction(opcx_supdate, new + * Integer(depth)); } + */ + + iter.add(addInst); + + // +-----+---------+------+-----------+ + // | ... | aupdate | inst | afterInst | + // +-----+---------+------+-----------+ + // ^prev ^next + + t = iter.previous(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+---------+------+-----------+ + // | ... | aupdate | inst | afterInst | + // +-----+---------+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == addInst, t + " != " + addInst); + + // +-----+---------+------+-----------+ + // | ... | aupdate | inst | afterInst | + // +-----+---------+------+-----------+ + // ^prev ^next + + t = iter.next(); + Assert.isTrue(t == inst, t + " != " + inst); + + // +-----+---------+------+-----------+ + // | ... | aupdate | inst | afterInst | + // +-----+---------+------+-----------+ + // ^prev ^next + + if (Main.VERBOSE > 2) { + System.out.println("Inserting " + addInst + + " before " + inst); + } + } else if (Main.VERBOSE > 2) { + System.out.println("Not inserting uc before " + inst); + } + } + } + } + } +} diff --git a/src/EDU/purdue/cs/bloat/decorate/Makefile b/src/EDU/purdue/cs/bloat/decorate/Makefile new file mode 100644 index 0000000..f9616df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/decorate/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + Main.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/decorate/package.html b/src/EDU/purdue/cs/bloat/decorate/package.html new file mode 100644 index 0000000..23b652c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/decorate/package.html @@ -0,0 +1,8 @@ + + + +

Contains a program that decorates a Java classfile with opcodes +used in a persistent Java virtual machine.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/diva/.cvsignore b/src/EDU/purdue/cs/bloat/diva/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/diva/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..23dd611 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/diva/InductionVarAnalyzer.java @@ -0,0 +1,514 @@ +/** + * 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. + * + * 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 + */ + +/* Demand-driven Induction Variable Analysis (diva)*/ +package EDU.purdue.cs.bloat.diva; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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 { + public static boolean DEBUG = false; + + SSAGraph ssaGraph; + + FlowGraph CFG; // Control flow graph being operated on + + HashMap IndStore; // Stores induction variables and + + // associated swizzlers + HashMap 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 Object get_swizzler(final int vn) { + final Iterator iter = IndStore.values().iterator(); + + while (iter.hasNext()) { + final 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 get_local(final int vn) { + final Iterator iter = LocalStore.keySet().iterator(); + + while (iter.hasNext()) { + final 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() { + final Iterator iter = IndStore.values().iterator(); + + while (iter.hasNext()) { + final 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(final 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 predacessor + * 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(final Swizzler indswz) { + final Iterator iter = CFG.preds(indswz.phi_block()).iterator(); + while (iter.hasNext()) { + final Block blk = (Block) iter.next(); + if (!indswz.phi_block().dominates(blk)) { + final 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 (InductionVarAnalyzer.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(final PhiJoinStmt phi, final 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? + */ + + final Iterator iter = cfg.preds(phi.block()).iterator(); + final Block pred1 = (Block) iter.next(); + final Block pred2 = (Block) iter.next(); + + if (pred1.dominates(phi.block()) && phi.block().dominates(pred2) + && (pred1 != phi.block())) { + if (InductionVarAnalyzer.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 (InductionVarAnalyzer.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); } + */ + + /** + * Performs DIVA on a CFG. + */ + public void transform(final FlowGraph cfg) { + ssaGraph = new SSAGraph(cfg); + CFG = cfg; + IndStore = new HashMap(); + LocalStore = new HashMap(); + changed = false; + + if (InductionVarAnalyzer.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(final List scc) { + if (InductionVarAnalyzer.DEBUG) { + System.out.println("SCC ="); + } + + final Iterator e = scc.iterator(); + + while (e.hasNext()) { + final Node v = (Node) e.next(); + if (InductionVarAnalyzer.DEBUG) { + System.out.println(" " + v + "{" + v.key() + "} " + + v.getClass()); + } + + v.visit(new TreeVisitor() { + public void visitPhiJoinStmt(final PhiJoinStmt phi) { + if (isMu(phi, CFG) != null) { + tgt = phi.target(); + if (InductionVarAnalyzer.DEBUG) { + System.out.println("IV:" + ind_var + " VN:" + + ind_var.valueNumber() + "\ninit:" + + ind_init + " target: " + tgt + + " VN: " + tgt.valueNumber()); + } + final Swizzler swz = new Swizzler(ind_var, tgt, + ind_init, phi.block()); + if (InductionVarAnalyzer.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 (InductionVarAnalyzer.DEBUG) { + System.out.println(" Mu: " + phi + "{" + + phi.key() + "}"); + } + } else { + if (InductionVarAnalyzer.DEBUG) { + System.out.println("Phi: " + phi + "{" + + phi.key() + "}"); + } + } + } + + public void visitLocalExpr(final 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 (InductionVarAnalyzer.DEBUG) { + System.out.println("stored ME: " + me + + " vn: " + me.valueNumber()); + } + } + + public void visitStaticFieldExpr( + final 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 (InductionVarAnalyzer.DEBUG) { + System.out.println("stored ME: " + me + + " vn: " + me.valueNumber()); + } + } + + public void visitIfCmpStmt(final 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 (InductionVarAnalyzer.DEBUG) { + displaySwizzler(indswz); + } + if (indswz.end_val() == null) { + indswz.set_end_val(cmp.right()); + set_term = true; + if (InductionVarAnalyzer.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 (InductionVarAnalyzer.DEBUG) { + displaySwizzler(indswz); + } + if (indswz.end_val() == null) { + indswz.set_end_val(cmp.left()); + set_term = true; + if (InductionVarAnalyzer.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(final SCStmt sc) { + Swizzler indswz; + MemExpr le = null; + + if (InductionVarAnalyzer.DEBUG) { + System.out.println("SC: array= " + sc.array() + + " VN:" + sc.array().valueNumber() + + "\nindex=" + sc.index() + " VN:" + + sc.index().valueNumber()); + } + + indswz = (Swizzler) get_swizzler(sc.index() + .valueNumber()); + if (indswz != null) { + if (InductionVarAnalyzer.DEBUG) { + displaySwizzler(indswz); + } + if (indswz.array() == null) { + le = get_local(sc.array().valueNumber()); + if ((le == null) + && (sc.array().def() != null)) { + le = get_local(sc.array().def() + .valueNumber()); + } + if (le != null) { + if (InductionVarAnalyzer.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 (InductionVarAnalyzer.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(final Tree tree) { + iter = tree.stmts().listIterator(); + + while (iter.hasNext()) { + final Stmt stmt = (Stmt) iter.next(); + stmt.visit(this); + } + } + + public void visitSCStmt(final SCStmt sc) { + Object dup2stmt; + if (sc.redundant()) { + + iter.remove(); + dup2stmt = iter.previous(); + iter.remove(); + if (InductionVarAnalyzer.DEBUG) { + System.out.println("Removed Redundant ASW: " + sc + + "\nand " + dup2stmt); + } + } + } + }); + } + + if (InductionVarAnalyzer.DEBUG) { + System.out.println("----------------After cfg.visit--------------"); + cfg.print(System.out); + } + } +} diff --git a/src/EDU/purdue/cs/bloat/diva/Main.java b/src/EDU/purdue/cs/bloat/diva/Main.java new file mode 100644 index 0000000..2e7c8d1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/diva/Main.java @@ -0,0 +1,637 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.diva; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.codegen.*; +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tbaa.*; +import EDU.purdue.cs.bloat.trans.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * Performs a number of analyses on the methods of some specified classes. + * However, it does not perform some of the optimizations that optimize.Main + * does. + * + * @see EDU.purdue.cs.bloat.optimize.Main + */ +public class Main { + static boolean DEBUG = false; + + static boolean VERBOSE = false; + + static boolean FORCE = false; + + static boolean CLOSURE = false; + + static boolean PRE = true; + + static boolean DCE = true; + + static boolean PROP = true; + + static boolean FOLD = true; + + static boolean STACK_ALLOC = false; + + static boolean COMPACT_ARRAY_INIT = true; + + static boolean ANNO = true; + + static String[] ARGS = null; + + static List SKIP = new ArrayList(); + + static List ONLY = new ArrayList(); + + static String METHOD = null; + + static BloatContext context = null; + + static ClassFileLoader loader = null; + + public static void main(final String[] args) { + try { + Main.loader = new ClassFileLoader(); + + List classes = new ArrayList(args.length); + boolean gotdir = false; + + Main.ARGS = args; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + Main.VERBOSE = true; + Main.loader.setVerbose(true); + } else if (args[i].equals("-debug")) { + Main.DEBUG = true; + Main.loader.setVerbose(true); + ClassFileLoader.DEBUG = true; + CompactArrayInitializer.DEBUG = true; + ClassEditor.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; + StackPRE.DEBUG = true; + ExprPropagation.DEBUG = true; + DeadCodeElimination.DEBUG = true; + } else if (args[i].equals("-help")) { + Main.usage(); + } else if (args[i].equals("-noanno")) { + Main.ANNO = false; + } else if (args[i].equals("-anno")) { + Main.ANNO = true; + } else if (args[i].equals("-preserve-debug")) { + MethodEditor.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("-nocompact-array-init")) { + Main.COMPACT_ARRAY_INIT = false; + } else if (args[i].equals("-compact-array-init")) { + Main.COMPACT_ARRAY_INIT = true; + } else if (args[i].equals("-nostack-alloc")) { + Main.STACK_ALLOC = false; + } else if (args[i].equals("-stack-alloc")) { + Main.STACK_ALLOC = true; + } else if (args[i].equals("-peel-loops")) { + if (++i >= args.length) { + Main.usage(); + } + + final 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) { + Main.usage(); + } + } catch (final NumberFormatException ex) { + Main.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) { + Main.usage(); + } + + Main.METHOD = args[i]; + } else if (args[i].equals("-print-flow-graph")) { + FlowGraph.PRINT_GRAPH = true; + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + Main.loader.setClassPath(classpath); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.SKIP.add(pkg); + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.ONLY.add(pkg); + } else if (args[i].equals("-nodce")) { + Main.DCE = false; + } else if (args[i].equals("-noprop")) { + Main.PROP = false; + } else if (args[i].equals("-nopre")) { + Main.PRE = false; + } else if (args[i].equals("-dce")) { + Main.DCE = true; + } else if (args[i].equals("-prop")) { + Main.PROP = true; + } else if (args[i].equals("-pre")) { + Main.PRE = true; + } else if (args[i].equals("-closure")) { + Main.CLOSURE = true; + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + } else if (args[i].equals("-f")) { + Main.FORCE = true; + } else if (args[i].startsWith("-")) { + Main.usage(); + } else if (i == args.length - 1) { + final 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); + } + + Main.loader.setOutputDir(f); + gotdir = true; + } else { + classes.add(args[i]); + } + } + + if (!gotdir) { + Main.usage(); + } + + if (classes.size() == 0) { + Main.usage(); + } + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + Main.loader.loadClass(name); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + Main.context = new CachingBloatContext(Main.loader, classes, + Main.CLOSURE); + + if (!Main.CLOSURE) { + final Iterator e = classes.iterator(); + + while (e.hasNext()) { + final String name = (String) e.next(); + Main.editClass(name); + } + } else { + classes = null; + + final Iterator e = Main.context.getHierarchy().classes() + .iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + Main.editClass(t.className()); + } + } + } + } catch (final ExceptionInInitializerError ex) { + ex.printStackTrace(); + System.out.println(ex.getException()); + } + } + + private static void usage() { + System.err + .println("Usage: 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 -[no]prop perform copy and constant propagation" + + ""); + System.exit(0); + } + + private static void editClass(final String className) { + ClassFile classFile; + + try { + classFile = (ClassFile) Main.loader.loadClass(className); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + return; + } + + if (!Main.FORCE) { + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (Main.VERBOSE) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (Main.DEBUG) { + classFile.print(System.out); + } + + final ClassEditor c = Main.context.editClass(classFile); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (Main.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < Main.ONLY.size(); i++) { + final String pkg = (String) Main.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 < Main.SKIP.size(); i++) { + final String pkg = (String) Main.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (Main.VERBOSE) { + System.out.println("Skipping " + c.type().className()); + } + + Main.context.release(classFile); + return; + } + + if (Main.VERBOSE) { + System.out.println("Optimizing " + c.type().className()); + } + + final MethodInfo[] methods = c.methods(); + + for (int j = 0; j < methods.length; j++) { + final MethodEditor m; + + try { + m = Main.context.editMethod(methods[j]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + if ((Main.METHOD != null) && !m.name().equals(Main.METHOD)) { + Main.context.release(methods[j]); + continue; + } + + if (Main.DEBUG) { + m.print(System.out); + } + + if (m.isNative() || m.isAbstract()) { + Main.context.release(methods[j]); + continue; + } + + if (Main.COMPACT_ARRAY_INIT) { + CompactArrayInitializer.transform(m); + + if (Main.DEBUG) { + System.out.println("---------- After compaction:"); + m.print(System.out); + System.out.println("---------- end print"); + } + } + + FlowGraph cfg; + + try { + cfg = new FlowGraph(m); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + Main.context.release(methods[j]); + continue; + } + + SSA.transform(cfg); + + if (FlowGraph.DEBUG) { + System.out.println("---------- After SSA:"); + cfg.print(System.out); + System.out.println("---------- end print"); + } + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + // Do copy propagation first to get rid of all the extra copies + // inserted for dups. If they're left it, it really slows down + // value numbering. + if (Main.PROP) { + if (Main.DEBUG) { + System.out.println("-------Before Copy Propagation-------"); + } + + final ExprPropagation copy = new ExprPropagation(cfg); + copy.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + if (Main.DEBUG) { + System.out.println("--------After Copy Propagation-------"); + cfg.print(System.out); + } + } + + if (Main.DCE) { + if (Main.DEBUG) { + System.out.println("-----Before Dead Code Elimination----"); + } + + final DeadCodeElimination dce = new DeadCodeElimination(cfg); + dce.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + if (Main.DEBUG) { + System.out.println("-----After Dead Code Elimination-----"); + cfg.print(System.out); + } + } + + if (Main.DEBUG) { + System.out.println("---------Doing type inference--------"); + } + + TypeInference.transform(cfg, Main.context.getHierarchy()); + + if (Main.DEBUG) { + System.out.println("--------Doing value numbering--------"); + } + + (new ValueNumbering()).transform(cfg); + + if (Main.FOLD) { + if (Main.DEBUG) { + System.out.println("--------Before Value Folding---------"); + } + + (new ValueFolding()).transform(cfg); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("---------After Value Folding---------"); + cfg.print(System.out); + } + } + + if (Main.PRE) { + if (Main.DEBUG) { + System.out.println("-------------Before SSAPRE-----------"); + } + + final SSAPRE pre = new SSAPRE(cfg, Main.context); + pre.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("-------------After SSAPRE------------"); + cfg.print(System.out); + } + } + + if (Main.FOLD) { + if (Main.DEBUG) { + System.out.println("--------Before Value Folding---------"); + } + + (new ValueFolding()).transform(cfg); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("---------After Value Folding---------"); + cfg.print(System.out); + } + } + + if (Main.PROP) { + if (Main.DEBUG) { + System.out.println("-------Before Copy Propagation-------"); + } + + final ExprPropagation copy = new ExprPropagation(cfg); + copy.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("--------After Copy Propagation-------"); + cfg.print(System.out); + } + } + + if (Main.DCE) { + if (Main.DEBUG) { + System.out.println("-----Before Dead Code Elimination----"); + } + + final DeadCodeElimination dce = new DeadCodeElimination(cfg); + dce.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("-----After Dead Code Elimination-----"); + cfg.print(System.out); + } + } + + (new PersistentCheckElimination()).transform(cfg); + (new InductionVarAnalyzer()).transform(cfg); + + /* + * if (STACK_ALLOC) { if (DEBUG) { + * System.out.println("------------Before StackPRE----------"); } + * + * StackPRE pre = new StackPRE(cfg); pre.transform(); + * + * if (DEBUG) { cfg.visit(new VerifyCFG()); } + * + * if (DEBUG) { System.out.println("------------After + * StackPRE-----------"); cfg.print(System.out); } } + */ + + cfg.commit(); + + Peephole.transform(m); + + Main.context.commit(methods[j]); + } + + if (Main.ANNO) { + String s = "Optimized with: EDU.purdue.cs.bloat.diva.Main"; + + for (int i = 0; i < Main.ARGS.length; i++) { + if ((Main.ARGS[i].indexOf(' ') >= 0) + || (Main.ARGS[i].indexOf('\t') >= 0) + || (Main.ARGS[i].indexOf('\r') >= 0) + || (Main.ARGS[i].indexOf('\n') >= 0)) { + s += " '" + Main.ARGS[i] + "'"; + } else { + s += " " + Main.ARGS[i]; + } + } + + System.out.println(s); + // c.constants().addConstant(Constant.UTF8, s); + } + + Main.context.commit(classFile); + } +} diff --git a/src/EDU/purdue/cs/bloat/diva/Makefile b/src/EDU/purdue/cs/bloat/diva/Makefile new file mode 100644 index 0000000..af4c0a1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/diva/Makefile @@ -0,0 +1,24 @@ +# 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. +# +# 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 + + +CLASS = \ + InductionVarAnalyzer.class\ + Main.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/dump/.cvsignore b/src/EDU/purdue/cs/bloat/dump/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/dump/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/dump/Main.java b/src/EDU/purdue/cs/bloat/dump/Main.java new file mode 100644 index 0000000..9a39ab8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/dump/Main.java @@ -0,0 +1,252 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.dump; + +import java.util.*; + +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Prints the contents of a Java classfile to the console. + */ +public class Main implements Opcode { + public static void main(final String[] args) { + final ClassFileLoader loader = new ClassFileLoader(); + String className = null; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-help")) { + Main.usage(); + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.setClassPath(classpath); + } else if (args[i].startsWith("-")) { + Main.usage(); + } else { + if (className != null) { + Main.usage(); + } + className = args[i]; + } + } + + if (className == null) { + Main.usage(); + } + + ClassInfo info = null; + + try { + info = loader.loadClass(className); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + System.exit(1); + } + + final Collection classes = new ArrayList(1); + classes.add(className); + + final BloatContext context = new CachingBloatContext(loader, classes, + true); + + if (info != null) { + Main.printClass(context, info); + } + } + + private static void usage() { + System.err + .println("Usage: java EDU.purdue.cs.bloat.dump.Main " + + "\n [-options] class" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -classpath " + + "\n list directories in which to look for classes"); + System.exit(0); + } + + private static void printClass(final EditorContext context, + final ClassInfo info) { + final ClassEditor c = context.editClass(info); + + if (c.isPublic()) { + System.out.print("public "); + } else if (c.isPrivate()) { + System.out.print("private "); + } else if (c.isProtected()) { + System.out.print("protected "); + } + + if (c.isStatic()) { + System.out.print("static "); + } + + if (c.isFinal()) { + System.out.print("final "); + } + + if (c.isInterface()) { + System.out.print("interface "); + } else if (c.isAbstract()) { + System.out.print("abstract class "); + } else { + System.out.print("class "); + } + + System.out.print(c.type().className()); + + if (c.superclass() != null) { + System.out.print(" extends " + c.superclass().className()); + } + + final Type[] interfaces = c.interfaces(); + + for (int i = 0; i < interfaces.length; i++) { + if (i == 0) { + System.out.print(" implements"); + } else { + System.out.print(","); + } + + System.out.print(" " + interfaces[i].className()); + } + + System.out.println(); + System.out.println("{"); + + final FieldInfo[] fields = c.fields(); + + for (int i = 0; i < fields.length; i++) { + FieldEditor f = null; + + try { + f = context.editField(fields[i]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + System.exit(1); + } + + System.out.print(" "); + + if (f.isPublic()) { + System.out.print("public "); + } else if (f.isPrivate()) { + System.out.print("private "); + } else if (f.isProtected()) { + System.out.print("protected "); + } + + if (f.isTransient()) { + System.out.print("transient "); + } + + if (f.isVolatile()) { + System.out.print("volatile "); + } + + if (f.isStatic()) { + System.out.print("static "); + } + + if (f.isFinal()) { + System.out.print("final "); + } + + System.out.println(f.type() + " " + f.name()); + + context.release(fields[i]); + } + + if (fields.length != 0) { + System.out.println(); + } + + final MethodInfo[] methods = c.methods(); + + for (int i = 0; i < methods.length; i++) { + MethodEditor m = null; + + try { + m = context.editMethod(methods[i]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + System.exit(1); + } + + if (i != 0) { + System.out.println(); + } + + System.out.print(" "); + + if (m.isPublic()) { + System.out.print("public "); + } else if (m.isPrivate()) { + System.out.print("private "); + } else if (m.isProtected()) { + System.out.print("protected "); + } + + if (m.isNative()) { + System.out.print("native "); + } + + if (m.isSynchronized()) { + System.out.print("synchronized "); + } + + if (m.isAbstract()) { + System.out.print("abstract "); + } + + if (m.isStatic()) { + System.out.print("static "); + } + + if (m.isFinal()) { + System.out.print("final "); + } + + System.out.println(m.type() + " " + m.name()); + + final Iterator iter = m.code().iterator(); + + while (iter.hasNext()) { + final Object obj = iter.next(); + System.out.println(" " + obj); + } + + context.release(methods[i]); + } + + System.out.println("}"); + + context.release(info); + } +} diff --git a/src/EDU/purdue/cs/bloat/dump/Makefile b/src/EDU/purdue/cs/bloat/dump/Makefile new file mode 100644 index 0000000..f9616df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/dump/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + Main.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/dump/package.html b/src/EDU/purdue/cs/bloat/dump/package.html new file mode 100644 index 0000000..2e16342 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/dump/package.html @@ -0,0 +1,8 @@ + + + +

Contains a program that prints the contents of a Java classfile to +the console.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/editor/.cvsignore b/src/EDU/purdue/cs/bloat/editor/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/editor/ClassEditor.java b/src/EDU/purdue/cs/bloat/editor/ClassEditor.java new file mode 100644 index 0000000..2046476 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/ClassEditor.java @@ -0,0 +1,551 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * A ClassEditor provides finer-grain access to a class than a CLassInfo object + * does. A ClassEditor takes a ClassInfo and extracts the class's constant pool, + * type, super class type, and the types of its interfaces. When editing is + * finished, changes are committed with the commit method. + * + * @see ClassInfo + * @see MethodEditor + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class ClassEditor { + public static boolean DEBUG = Boolean.getBoolean("ClassEditor.DEBUG"); + + private ConstantPool constants; // A copy of the constant pool of the class + + // being edited. + private ClassInfo classInfo; // (A representation of) the class being + // edited + + private Type type; // An index into constant pool (descriptors) that + + private Type superclass; // specifies the class, superclass, and + // interfaces + + private Type[] interfaces; // of the class being edited. + + private EditorContext context; // Use to edit classes and methods + + private boolean dirty; // Has the class been modified? + + /** + * Constructor. Create a new ClassEditor based on information in a ClassInfo + * object. It extracts the class's constant pool, and the types of the + * class, its superclass, and any interfaces it implements. + * + * @param context + * The EditorContext used to edit fields and methods. + * @param classInfo + * The ClassInfo structure of the class to edit. + * + * @see EDU.purdue.cs.bloat.reflect.ClassInfo + * @see ConstantPool + * @see Type + */ + public ClassEditor(final EditorContext context, final ClassInfo classInfo) { + this.context = context; + this.classInfo = classInfo; + this.dirty = false; + + // Extract the constant pool from the ClassInfo + constants = new ConstantPool(classInfo.constants()); + + int index; + + // Load information (such as the indices of the class, superclass, + // and the interfaces) about the class being edited from its + // constant pool. + + index = classInfo.classIndex(); + type = (Type) constants.constantAt(index); + + index = classInfo.superclassIndex(); + superclass = (Type) constants.constantAt(index); + + final int ifs[] = classInfo.interfaceIndices(); + interfaces = new Type[ifs.length]; + + for (int i = 0; i < ifs.length; i++) { + interfaces[i] = (Type) constants.constantAt(ifs[i]); + } + + if (ClassEditor.DEBUG) { + System.out.println("Editing class " + type); + } + + this.setDirty(false); + } + + /** + * Creates a new ClassEditor for editing a class (or + * interface) from scratch. This constructor should not be invoked direcly. + * Use {@link EditorContext#newClass(int, String, Type, Type[])} instead. + */ + public ClassEditor(final EditorContext context, final int modifiers, + final String className, Type superType, Type[] interfaces) { + + if (className == null) { + final String s = "Cannot have a null class name"; + throw new IllegalArgumentException(s); + } + + if (superType == null) { + superType = Type.OBJECT; + } + + if (interfaces == null) { + interfaces = new Type[0]; + } + + if (ClassEditor.DEBUG) { + System.out.println("Creating new class " + className + " extends " + + superType.className()); + } + + this.context = context; + this.superclass = superType; + this.interfaces = interfaces; + + final ConstantPool cp = new ConstantPool(); + this.constants = cp; + + this.type = Type.getType(Type.classDescriptor(className)); + final int classNameIndex = cp.getClassIndex(this.type); + final int superTypeIndex = cp.getClassIndex(superType); + final int[] interfaceIndexes = new int[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + interfaceIndexes[i] = cp.getClassIndex(interfaces[i]); + } + + this.classInfo = context.newClassInfo(modifiers, classNameIndex, + superTypeIndex, interfaceIndexes, cp.getConstantsList()); + + this.dirty = true; + } + + /** + * Returns true if the class has been modified. + */ + public boolean isDirty() { + return (this.dirty); + } + + /** + * Sets this class's dirty flag. The dirty flag is true if the + * class has been modified. + */ + public void setDirty(final boolean dirty) { + this.dirty = dirty; + } + + /** + * Returns the name of the class represented by this ClassEditor. + */ + public String name() { + return (this.classInfo().name()); + } + + /** + * Obtain the EditorContext for this ClassEditor. + */ + public EditorContext context() { + return context; + } + + /** + * Get the ClassInfo object representing the class that being edited. + */ + public ClassInfo classInfo() { + return classInfo; + } + + public boolean isPublic() { + return (classInfo.modifiers() & Modifiers.PUBLIC) != 0; + } + + public boolean isPrivate() { + return (classInfo.modifiers() & Modifiers.PRIVATE) != 0; + } + + public boolean isProtected() { + return (classInfo.modifiers() & Modifiers.PROTECTED) != 0; + } + + public boolean isStatic() { + return (classInfo.modifiers() & Modifiers.STATIC) != 0; + } + + public boolean isFinal() { + return (classInfo.modifiers() & Modifiers.FINAL) != 0; + } + + public boolean isSuper() { + return (classInfo.modifiers() & Modifiers.SUPER) != 0; + } + + public boolean isAbstract() { + return (classInfo.modifiers() & Modifiers.ABSTRACT) != 0; + } + + public boolean isInterface() { + return (classInfo.modifiers() & Modifiers.INTERFACE) != 0; + } + + public void setPublic(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PUBLIC; + } else { + modifiers &= ~Modifiers.PUBLIC; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setPrivate(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PRIVATE; + } else { + modifiers &= ~Modifiers.PRIVATE; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setProtected(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PROTECTED; + } else { + modifiers &= ~Modifiers.PROTECTED; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setStatic(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.STATIC; + } else { + modifiers &= ~Modifiers.STATIC; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setFinal(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.FINAL; + } else { + modifiers &= ~Modifiers.FINAL; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setSuper(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.SUPER; + } else { + modifiers &= ~Modifiers.SUPER; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setAbstract(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.ABSTRACT; + } else { + modifiers &= ~Modifiers.ABSTRACT; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + public void setInterface(final boolean flag) { + int modifiers = classInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.INTERFACE; + } else { + modifiers &= ~Modifiers.INTERFACE; + } + + classInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * Sets the Type (descriptor) object for the class. + * + * @param type + * A Type. + */ + public void setType(final Type type) { + this.type = type; + Assert.isTrue(type.isObject(), "Cannot set class type to " + type); + this.setDirty(true); + } + + /** + * Returns the Type (descriptor) for the class. + */ + public Type type() { + return type; + } + + /** + * Returns a Type object for the class's superclass. + */ + public Type superclass() { + return superclass; + } + + /** + * Adds an interface of the given class to the set of interfaces that the + * class implements. + * + * @throws IllegalArgumentException interfaceClass is not an + * interface + */ + public void addInterface(final Class interfaceClass) { + if (!interfaceClass.isInterface()) { + final String s = "Cannot add non-interface type: " + + interfaceClass.getName(); + throw new IllegalArgumentException(s); + } + + addInterface(Type.getType(interfaceClass)); + } + + /** + * Adds an interface of a given Type to the set of interfaces that the class + * implements. + */ + public void addInterface(final Type interfaceType) { + // // The interface must have an index in the constant pool + // this.constants().getClassIndex(interfaceType); + + final Type[] interfaces = new Type[this.interfaces.length + 1]; + for (int i = 0; i < this.interfaces.length; i++) { + interfaces[i] = this.interfaces[i]; + } + interfaces[interfaces.length - 1] = interfaceType; + this.setInterfaces(interfaces); + } + + /** + * Returns the interfaces the class implements. + * + * @param interfaces + * An array of Types. + */ + public void setInterfaces(final Type[] interfaces) { + this.interfaces = interfaces; + this.setDirty(true); + } + + /** + * Returns the interfaces the class implements. + */ + public Type[] interfaces() { + return interfaces; + } + + /** + * Returns the modifiers of the class. The values correspond to the + * constants in the Modifiers class. + * + * @return A bit vector of modifier flags for the class. + * @see Modifiers + */ + public int modifiers() { + return classInfo.modifiers(); + } + + /** + * Returns an array of FieldInfo structures for each field in the + * class. + */ + public FieldInfo[] fields() { + return classInfo.fields(); + } + + /** + * Returns an array of MethodInfo structures for each method in the class. + */ + public MethodInfo[] methods() { + return classInfo.methods(); + } + + /** + * Returns the constant pool for the class. + */ + public ConstantPool constants() { + return constants; + } + + /** + * Commit any changes to the class since creation time. Note that committal + * will occur regardless of whether or not the class is dirty. + */ + public void commit() { + commitOnly(null, null); + } + + /** + * Commits only certain methods and fields. Note that committal will occur + * regardless of whether or not the class is dirty. + * + * @param methods + * Methods (MethodInfos) to commit. If null, + * all methods are committed. + * @param fields + * Fields (FieldInfos) to commit. If null, + * all fields are committed. + */ + public void commitOnly(final Set methods, final Set fields) { + classInfo.setClassIndex(constants.addConstant(Constant.CLASS, type)); + classInfo.setSuperclassIndex(constants.addConstant(Constant.CLASS, + superclass)); + + final int ifs[] = new int[interfaces.length]; + + for (int i = 0; i < ifs.length; i++) { + ifs[i] = constants.addConstant(Constant.CLASS, interfaces[i]); + } + + classInfo.setInterfaceIndices(ifs); + + classInfo.setConstants(constants.constants()); + + classInfo.commitOnly(methods, fields); + + // This class is no longer dirty + this.setDirty(false); + } + + /** + * This class is visited by an EditorVisitor. First, this + * ClassEditor itself is visited. Then, all of this class's + * fields (FieldEditors) are visited. Finally, each of this + * class's methods (MethodEditors) are visited. Constructors + * are visited before regular methods. + */ + public void visit(final EditorVisitor visitor) { + // First visit ourself + visitor.visitClassEditor(this); + + final EditorContext context = this.context(); + + // Visit each field + final FieldInfo[] fields = this.fields(); + for (int i = 0; i < fields.length; i++) { + final FieldEditor fieldEditor = context.editField(fields[i]); + visitor.visitFieldEditor(fieldEditor); + context.release(fields[i]); + } + + // Visit each method + final ArrayList regularMethods = new ArrayList(); + final MethodInfo[] methods = this.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor methodEditor = context.editMethod(methods[i]); + + if (methodEditor.name().charAt(0) != '<') { + regularMethods.add(methods[i]); + + } else { + visitor.visitMethodEditor(methodEditor); + } + + context.release(methods[i]); + } + + final Iterator iter = regularMethods.iterator(); + while (iter.hasNext()) { + final MethodInfo info = (MethodInfo) iter.next(); + final MethodEditor me = context.editMethod(info); + visitor.visitMethodEditor(me); + context.release(info); + } + } + + /** + * Two ClassEditors are equal if they edit the same class. + */ + public boolean equals(final Object o) { + if (o instanceof ClassEditor) { + final ClassEditor other = (ClassEditor) o; + if (!other.type().equals(this.type())) { + return (false); + } + + return (true); + } + + return (false); + } + + /** + * A ClassEditor's hash code is based upon the hash code of the + * name of the class it edits. + */ + public int hashCode() { + return (this.name().hashCode()); + } + + public String toString() { + return (this.type().toString()); + } + +} 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..9ee509b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/ClassHierarchy.java @@ -0,0 +1,1270 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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 = false; + + 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; + + LinkedList worklist; + + Set inWorklist; + + private void db(final String s) { + if (ClassHierarchy.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(final EditorContext context, + final Collection initial, final boolean closure) { + this.context = context; + this.closure = closure; + + classes = new HashSet(); + extendsGraph = new Graph(); + implementsGraph = new Graph(); + + worklist = new LinkedList(); + inWorklist = new HashSet(); + + this.resolvesToCache = new HashMap(); + + // Need new ArrayList to avoid ConcurrentModificationException + final Iterator iter = new ArrayList(initial).iterator(); + + while (iter.hasNext()) { + final String name = (String) iter.next(); + addClass(name); + } + } + + /** + * Adds a class of a given name to the ClassHierarchy. + */ + public void addClassNamed(final String name) { + addClass(name); + } + + /** + * 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(final Type type) { + final TypeNode node = getExtendsNode(type); + + if (node != null) { + final List list = new ArrayList(extendsGraph.preds(node)); + + final ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + final TypeNode v = (TypeNode) iter.next(); + iter.set(v.type); + } + + 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(final Type type) { + final TypeNode node = getExtendsNode(type); + + if (node != null) { + final Collection succs = extendsGraph.succs(node); + + final Iterator iter = succs.iterator(); + + if (iter.hasNext()) { + final 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(final Type type) { + final TypeNode node = getImplementsNode(type); + + if (node != null) { + final List list = new ArrayList(implementsGraph.succs(node)); + + final ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + final TypeNode v = (TypeNode) iter.next(); + iter.set(v.type); + } + + 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(final Type type) { + final TypeNode node = getImplementsNode(type); + + if (node != null) { + final List list = new ArrayList(implementsGraph.preds(node)); + + final ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + final TypeNode v = (TypeNode) iter.next(); + iter.set(v.type); + } + + return list; + } + + return new ArrayList(); + } + + /** + * Returns whether or not a is a subclass of b. + */ + public boolean subclassOf(final Type a, final Type b) { + // Is a <= b? + Assert.isTrue(a.isReference() && b.isReference(), "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.isNull()) { + return a.isNull(); + } + + if (a.isArray()) { + if (b.isArray()) { + // Both reference arrays. + // a <= b -> a[] <= b[] + if (a.elementType().isReference() + && b.elementType().isReference()) { + + return subclassOf(a.elementType(), b.elementType()); + } + + // a[] <= a[]: true + return a.elementType().equals(b.elementType()); + } + + // Only one is an array (and b is not Object--tested above). + return false; + } + + // a <= b[]: false + if (b.isArray()) { + // 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(final Type type) { + final GraphNode node = extendsGraph.getNode(type); + + if ((node == null) && type.isObject()) { + this.addClassNamed(type.className()); + } + + 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(final Type type) { + final GraphNode node = implementsGraph.getNode(type); + + if ((node == null) && type.isObject()) { + this.addClassNamed(type.className()); + } + + return ((TypeNode) implementsGraph.getNode(type)); + } + + /** + * Adds a type (and all types it references) to the extends and implements + * graphs. + */ + private void addClass(final String name) { + Type type = Type.getType(Type.classDescriptor(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 = (Type) 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 = 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 + ClassEditor c; + + try { + c = context.editClass(type.className()); + + } catch (final ClassNotFoundException ex) { + if (ClassHierarchy.RELAX) { + continue; + } + + throw new RuntimeException("Class not found: " + + ex.getMessage()); + } + + final Type[] interfaces = c.interfaces(); + + if (c.superclass() != null) { + // 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 = getExtendsNode(c.superclass()); + + if (superNode == null) { + superNode = new TypeNode(c.superclass()); + extendsGraph.addNode(c.superclass(), 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(Type.OBJECT)) { + extendsGraph.addEdge(extendsNode, superNode); + } + } + + } else { + // Only java.lang.Object has no superclass + if (!type.equals(Type.OBJECT) && !ClassHierarchy.RELAX) { + throw new RuntimeException("Null superclass for " + type); + } + } + + // 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++) { + final Type iType = interfaces[i]; + TypeNode iNode = 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++) { + final Type iType = interfaces[i]; + TypeNode iNode = getImplementsNode(iType); + if (iNode == null) { + iNode = new TypeNode(iType); + implementsGraph.addNode(iType, iNode); + } + implementsGraph.addEdge(implementsNode, iNode); + } + } + + if (c.superclass() != null) { + // Add the super type to the worklist + + // db("typeref " + type + " -> " + c.superclass()); + + addType(c.superclass()); + } + + for (int i = 0; i < c.interfaces().length; i++) { + // Add all of the interface types to the worklist + + // db("typeref " + type + " -> " + c.interfaces()[i]); + + addType(c.interfaces()[i]); + } + + if (!this.closure) { + context.release(c.classInfo()); + continue; + } + + for (int i = 0; i < c.methods().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. + final MethodInfo m = c.methods()[i]; + final int typeIndex = m.typeIndex(); + + final String desc = (String) c.constants() + .constantAt(typeIndex); + final Type t = Type.getType(desc); + + // db("typeref " + type + " -> " + t); + + addType(t); + } + + for (int i = 0; i < c.fields().length; i++) { + // Add the types of all of the fields + + final FieldInfo f = c.fields()[i]; + final int typeIndex = f.typeIndex(); + + final String desc = (String) c.constants() + .constantAt(typeIndex); + final Type t = Type.getType(desc); + + // db("typeref " + type + " -> " + t); + + addType(t); + } + + for (int i = 0; i < c.constants().numConstants(); i++) { + // Look through the constant pool for interesting (non-LONG + // and non-DOUBLE) constants and add them to the worklist. + + final int tag = c.constants().constantTag(i); + + if ((tag == Constant.LONG) || (tag == Constant.DOUBLE)) { + i++; + + } else if (tag == Constant.CLASS) { + final Type t = (Type) c.constants().constantAt(i); + + // db("typeref " + type + " -> " + t); + + addType(t); + + } else if (tag == Constant.NAME_AND_TYPE) { + final NameAndType t = (NameAndType) c.constants() + .constantAt(i); + + // db("typeref " + type + " -> " + t.type()); + + addType(t.type()); + } + } + + // We're done editing the class + context.release(c.classInfo()); + + } + + /* + * // Now that we've entered the class (and at least all of its // + * subclasses) into the hierarchy, notify superclasses that // they've + * been subclassses. This will invalidate the TypeNodes // in the + * dependence graph. DependenceGraph dg = + * BloatContext.getContext().dependenceGraph(); + * + * for(Type superclass = superclass(type); superclass != null; + * superclass = superclass(superclass)) { db("ClassHierarchy: + * Invalidating superclass " + superclass); + * + * EDU.purdue.cs.bloat.depend.TypeNode typeNode = + * dg.getTypeNode(superclass); typeNode.invalidate(); } + */ + } + + // 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(final Type type) { + if (type.isMethod()) { + // Add all of the types of the parameters and the return type + + final Type[] paramTypes = type.paramTypes(); + + for (int i = 0; i < paramTypes.length; i++) { + // db("typeref " + type + " -> " + paramTypes[i]); + + addType(paramTypes[i]); + } + + final Type returnType = type.returnType(); + + // db("typeref " + type + " -> " + returnType); + + addType(returnType); + + return; + } + + if (type.isArray()) { + // 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(type.elementType()); + + return; + } + + if (!type.isObject()) { + 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(final Type a, final Type b) { + Assert.isTrue(a.isReference() && b.isReference(), "Cannot intersect " + + a + " and " + b); + + if (a.equals(b)) { + return a; + } + + if (a.isNull() || b.isNull()) { + return Type.NULL; + } + + if (a.equals(Type.OBJECT)) { + return b; + } + + if (b.equals(Type.OBJECT)) { + return a; + } + + if (a.isArray()) { + if (b.isArray()) { + // Both reference arrays. + if (a.elementType().isReference() + && b.elementType().isReference()) { + + final Type t = intersectType(a.elementType(), b + .elementType()); + + if (t.isNull()) { + return Type.NULL; + } + + return t.arrayType(); + } + + // Only one is a reference array. + if (a.elementType().isReference() + || b.elementType().isReference()) { + return Type.NULL; + } + + // Both primitive arrays, not equal. + return Type.NULL; + } + + // Only one is an array. + return Type.NULL; + } + + if (b.isArray()) { + // 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(final Collection types) { + if (types.size() <= 0) { + return (Type.OBJECT); + } + + final 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(final Type a, final Type b) { + + if (a.equals(b)) { + return a; + } + + if (a.equals(Type.OBJECT) || b.equals(Type.OBJECT)) { + return Type.OBJECT; + } + + if (a.isNull()) { + return b; + } + + if (b.isNull()) { + return a; + } + + // Handle funky integral types introduced during type inferencing. + if ((a.isIntegral() || a.equals(ClassHierarchy.POS_BYTE) || a + .equals(ClassHierarchy.POS_SHORT)) + && (b.isIntegral() || b.equals(ClassHierarchy.POS_BYTE) || b + .equals(ClassHierarchy.POS_SHORT))) { + + final BitSet v1 = ClassHierarchy.typeToSet(a); + final BitSet v2 = ClassHierarchy.typeToSet(b); + v1.or(v2); + return (ClassHierarchy.setToType(v1)); + } + + Assert.isTrue(a.isReference() && b.isReference(), "Cannot union " + a + + " and " + b); + + if (a.isArray()) { + if (b.isArray()) { + // Both reference arrays. + if (a.elementType().isReference() + && b.elementType().isReference()) { + + final Type t = unionType(a.elementType(), b.elementType()); + return t.arrayType(); + } + + // Only one is a reference array. + if (a.elementType().isReference() + || b.elementType().isReference()) { + return Type.OBJECT; + } + + // Both primitive arrays, not equal. + return Type.OBJECT; + } + + // Only one is an array. + return Type.OBJECT; + } + + if (b.isArray()) { + // Only one is an array. + return Type.OBJECT; + } + + // Neither is an array. + final Set superOfA = new HashSet(); + final Set superOfB = new HashSet(); + + 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(final 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(final PrintWriter out, final int indent) { + final TypeNode objectNode = this.getExtendsNode(Type.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(final PrintWriter out, int indent) { + // There are multiple roots to the implements graph. + indent += 2; + final Iterator roots = this.implementsGraph.roots().iterator(); + while (roots.hasNext()) { + final 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(final Type iType, final PrintWriter out, + final boolean recurse, final int indent) { + final Iterator implementors = this.implementors(iType).iterator(); + while (implementors.hasNext()) { + final 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(final PrintWriter out, final 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(final Type classType, final PrintWriter out, + final boolean recurse, final int indent) { + final Iterator iter = this.subclasses(classType).iterator(); + while (iter.hasNext()) { + final 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. + */ + public boolean methodIsOverridden(final Type classType, + final NameAndType nat) { + final String methodName = nat.name(); + final Type methodType = nat.type(); + + db("ClassHierarchy: Is " + classType + "." + methodName + methodType + + " overridden?"); + + final Collection subclasses = this.subclasses(classType); + + final Iterator iter = subclasses.iterator(); + while (iter.hasNext()) { + final Type subclass = (Type) iter.next(); + + db("Examining subclass " + subclass); + + // Obtain a ClassEditor for the class + ClassEditor ce = null; + + try { + ce = context.editClass(subclass.className()); + + } catch (final ClassNotFoundException ex) { + db(ex.getMessage()); + return (true); + } + + // Examine each method in the subclass + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = context.editMethod(methods[i]); + if (me.name().equals(methodName) + && me.type().equals(methodType)) { + db(" " + methodName + methodType + " is overridden by " + + me.name() + me.type()); + context.release(ce.classInfo()); + return (true); // Method is overridden + } + } + + // Recurse over subclasses + if (methodIsOverridden(subclass, nat)) { + context.release(ce.classInfo()); + return (true); + } + + context.release(ce.classInfo()); + } + + // Got through all subclasses and method was not overridden + db(" NO!"); + + 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. + */ + public MemberRef methodInvoked(final Type receiver, final 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 + final MemberRef m = new MemberRef(type, method); + try { + context.editMethod(m); + return (m); + + } catch (final 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(final BitSet v) { + if (v.get(ClassHierarchy.MAX_INT)) { + return Type.INTEGER; + } + + if (v.get(ClassHierarchy.MAX_CHAR)) { + if (v.get(ClassHierarchy.MIN_INT) + || v.get(ClassHierarchy.MIN_SHORT) + || v.get(ClassHierarchy.MIN_BYTE)) { + return Type.INTEGER; + + } else { + return Type.CHARACTER; + } + } + + if (v.get(ClassHierarchy.MAX_SHORT)) { + if (v.get(ClassHierarchy.MIN_INT)) { + return Type.INTEGER; + + } else if (v.get(ClassHierarchy.MIN_SHORT) + || v.get(ClassHierarchy.MIN_BYTE)) { + return Type.SHORT; + + } else { + return ClassHierarchy.POS_SHORT; + } + } + + if (v.get(ClassHierarchy.MAX_BYTE)) { + if (v.get(ClassHierarchy.MIN_INT)) { + return Type.INTEGER; + + } else if (v.get(ClassHierarchy.MIN_SHORT)) { + return Type.SHORT; + + } else if (v.get(ClassHierarchy.MIN_BYTE)) { + return Type.BYTE; + + } else { + return ClassHierarchy.POS_BYTE; + } + } + + if (v.get(ClassHierarchy.MAX_BOOL)) { + if (v.get(ClassHierarchy.MIN_INT)) { + return Type.INTEGER; + + } else if (v.get(ClassHierarchy.MIN_SHORT)) { + return Type.SHORT; + + } else if (v.get(ClassHierarchy.MIN_BYTE)) { + return Type.BYTE; + + } else { + return Type.BOOLEAN; + } + } + + if (v.get(ClassHierarchy.MIN_INT)) { + return Type.INTEGER; + } + + if (v.get(ClassHierarchy.MIN_SHORT)) { + return Type.SHORT; + } + + if (v.get(ClassHierarchy.MIN_BYTE)) { + return Type.BYTE; + } + + return Type.BOOLEAN; + } + + /** + * Returns a BitSet representing the possible values of a given integral + * type. + */ + public static BitSet typeToSet(final Type type) { + final BitSet v = new BitSet(ClassHierarchy.MAX_INT); + int lo; + int hi; + + if (type.equals(Type.INTEGER)) { + lo = ClassHierarchy.MIN_INT; + hi = ClassHierarchy.MAX_INT; + + } else if (type.equals(Type.CHARACTER)) { + lo = ClassHierarchy.MIN_CHAR; + hi = ClassHierarchy.MAX_CHAR; + + } else if (type.equals(Type.SHORT)) { + lo = ClassHierarchy.MIN_SHORT; + hi = ClassHierarchy.MAX_SHORT; + + } else if (type.equals(ClassHierarchy.POS_SHORT)) { + lo = ClassHierarchy.ZERO; + hi = ClassHierarchy.MAX_SHORT; + + } else if (type.equals(Type.BYTE)) { + lo = ClassHierarchy.MIN_BYTE; + hi = ClassHierarchy.MAX_BYTE; + + } else if (type.equals(ClassHierarchy.POS_BYTE)) { + lo = ClassHierarchy.ZERO; + hi = ClassHierarchy.MAX_BYTE; + + } else if (type.equals(Type.BOOLEAN)) { + lo = ClassHierarchy.ZERO; + hi = ClassHierarchy.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 MemberRef method; + + /** + * The types with which the call resolves to the above method + */ + public HashSet 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(final MemberRef method) { + Set resolvesTo = (Set) this.resolvesToCache.get(method); + + if (resolvesTo == null) { + db("Resolving " + method); + + resolvesTo = new HashSet(); // All methods it could resolve to + final ResolvesToWith rtw = new ResolvesToWith(); + rtw.method = method; + rtw.rTypes = new HashSet(); + + // 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. + MethodEditor me = null; + try { + me = context.editMethod(method); + + } catch (final 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. Really? + 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.isConstructor())) { + 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. + final List types = new LinkedList(); + types.add(method.declaringClass()); + while (!types.isEmpty()) { + final Type type = (Type) types.remove(0); + + db(" Examining type " + type); + + ClassEditor ce = null; + try { + ce = context.editClass(type); + + } catch (final 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. + final Iterator subinterfaces = this.subclasses(type) + .iterator(); + while (subinterfaces.hasNext()) { + final Type subinterface = (Type) subinterfaces + .next(); + types.add(subinterface); + db(" Noting subinterface " + subinterface); + } + + final Iterator implementors = this.implementors(type) + .iterator(); + while (implementors.hasNext()) { + final Type implementor = (Type) implementors.next(); + types.add(implementor); + db(" Noting implementor " + implementor); + } + + } else { + // We've got a class. Does it override the method? + final NameAndType nat = method.nameAndType(); + final MethodInfo[] methods = ce.methods(); + boolean overridden = false; + for (int i = 0; i < methods.length; i++) { + final MethodEditor over = context + .editMethod(methods[i]); + final MemberRef ref = over.memberRef(); + if (ref.nameAndType().equals(nat)) { + // This class implements the method. + if (!method.declaringClass().equals(type)) { + // Make a recursive call. + db(" Class " + type + " overrides " + + method); + resolvesTo.addAll(resolvesToWith(ref)); + 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. + final Iterator subclasses = this.subclasses(type) + .iterator(); + while (subclasses.hasNext()) { + final 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/CodeArray.java b/src/EDU/purdue/cs/bloat/editor/CodeArray.java new file mode 100644 index 0000000..c0706bb --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/CodeArray.java @@ -0,0 +1,2408 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * CodeArray converts an array of Instructions into an array of bytes suitable + * for saving to a MethodInfo with setCode. + * + *

+ * + * The byte array is returned by calling the array method. + * + *

+ * + * This code assumes no branch will be longer than 65536 bytes. + * + * @see Instruction + * @see MethodInfo + * @see MethodInfo#setCode + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class CodeArray implements InstructionVisitor, Opcode { + public static boolean DEBUG = Boolean.getBoolean("CodeArray.DEBUG"); + + private ByteCell codeTail; // Linked list of ByteCells representing code + + private int codeLength; // Number of bytes in method + + private Map branches; + + private Map longBranches; + + private Map branchInsts; + + private Map labels; // Labels mapped to their offsets + + private int lastInst; // Offset of last (most recent) instrucion + + private int maxStack; // Max stack height + + private int stackHeight; // Current stack height + + private int maxLocals; // Max number of local variables + + private ConstantPool constants; + + private MethodEditor method; + + private boolean longBranch; // Do we use long (wide) jumps? + + private List insts; + + /** + * Create the byte array for a method. + * + * @param method + * The method being edited. + * @param constants + * The constant pool of the class. + * @param insts + * A List of Instructions and Labels to convert to a byte array. + * @see MethodEditor + * @see ConstantPool + * @see Instruction + * @see Label + */ + public CodeArray(final MethodEditor method, final ConstantPool constants, + final List insts) { + this.constants = constants; + this.method = method; + this.insts = insts; + this.maxStack = 0; + this.maxLocals = 0; + + longBranch = false; + buildCode(); + } + + /** + * Examine the method's Labels and Instructions. Keep track of such things + * as the height of the stack at each instruction and to where subroutines + * return. The ultimate goal is to compute the max stack height of this + * method. This computation is complicated by subroutines that may be + * invoked at a variety of stack heights. + */ + private void buildCode() { + codeTail = null; + codeLength = 0; + branches = new HashMap(); + longBranches = new HashMap(); + branchInsts = new HashMap(); + labels = new HashMap(); + + // We need at least enought locals to store the parameters + maxLocals = method.type().stackHeight(); + + if (!method.isStatic()) { + // One more for the this pointer + maxLocals++; + } + + stackHeight = 0; + + final Map labelPos = new HashMap(); // Maps Labels to their code offsets + final int[] heights = new int[insts.size()]; // Stack height at each + // inst + + // Maps Labels that begin jsrs to their return targets. Maps ret + // instructions to the subroutine from which they return. + final Map retTargets = new HashMap(); + final Map retInsts = new HashMap(); + + // Print the code we're dealing with + if (CodeArray.DEBUG) { + System.out.println("Building code for " + + method.declaringClass().name() + "." + method.name()); + final Iterator iter = insts.iterator(); + while (iter.hasNext()) { + final Object o = iter.next(); + System.out.println(" " + o); + } + } + + // Build the bytecode array, assuming each basic block begins with + // stack height 0. We'll fix up the heights later. + final Iterator iter = insts.iterator(); + int i = 0; // Which instruction are we at? + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (ce instanceof Label) { + final Label label = (Label) ce; + + // A Label starts a new basic block. Reset the stack height. + + stackHeight = 0; + labelPos.put(label, new Integer(i)); + + addLabel(label); + heights[i++] = stackHeight; + + // If this label starts a subroutine (i.e. is the target of + // jsr instruction), then make not of it. + if (retTargets.containsKey(label)) { + } + + } else if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + // Visit this instruction to compute the current stack height + inst.visit(this); + + if (inst.isJsr()) { + // Make sure that the jsr is not the last instruction in the + // method. If it was, where would we return to? Make note + // of the return target (the Label following the jsr). + + heights[i++] = stackHeight; + + Assert.isTrue(iter.hasNext(), inst + + " found at end of method"); + + final Object x = iter.next(); + + Assert.isTrue(x instanceof Label, inst + + " not followed by label"); + + final Label sub = (Label) inst.operand(); + final Label target = (Label) x; + + // Maintain a mapping between a subroutine (the Label that + // begins it) and all return targets. + Set targets = (Set) retTargets.get(sub); + if (targets == null) { + targets = new HashSet(); + retTargets.put(sub, targets); + } + targets.add(target); + + stackHeight = 0; + labelPos.put(target, new Integer(i)); + + addLabel(target); + heights[i++] = stackHeight; + + } else { + heights[i++] = stackHeight; + } + + } else { + // Something bad in instruction list + throw new IllegalArgumentException(); + } + } + + // Sorry, but we have to make another forward pass over some of + // the code to determine the subroutine from which a given ret + // instruction returns. + final Iterator subLabels = retTargets.keySet().iterator(); + while (subLabels.hasNext()) { + final Label subLabel = (Label) subLabels.next(); + final int pos = insts.indexOf(subLabel); + Assert.isTrue(pos != -1, "Label " + subLabel + " not found"); + boolean foundRet = false; + final ListIterator liter = insts.listIterator(pos); + while (liter.hasNext()) { + final Object o = liter.next(); + if (o instanceof Instruction) { + final Instruction inst = (Instruction) o; + if (inst.isRet()) { + retInsts.put(inst, subLabel); + foundRet = true; + break; + } + } + } + Assert.isTrue(foundRet, "No ret for subroutine " + subLabel); + } + + if (CodeArray.DEBUG) { + // Print subroutine to return target mapping + System.out.println("Subroutines and return targets:"); + final Iterator subs = retTargets.keySet().iterator(); + while (subs.hasNext()) { + final Label sub = (Label) subs.next(); + System.out.print(" " + sub + ": "); + final Set s = (Set) retTargets.get(sub); + Assert.isTrue(s != null, "No return targets for " + sub); + final Iterator rets = s.iterator(); + while (rets.hasNext()) { + final Label ret = (Label) rets.next(); + System.out.print(ret.toString()); + if (rets.hasNext()) { + System.out.print(", "); + } + } + System.out.println(""); + } + } + + // Fix up the stack heights by propagating the heights at each catch + // and each branch to their targets. Visit the blocks + // depth-first. Remember that the classfile requires the maximum + // stack height. I would assume that is why we do all of this + // stack height calculation stuff. + + final Set visited = new HashSet(); // Labels that we've seen + final Stack stack = new Stack(); // Stack of HeightRecords + Label label; + + // Start with the first Label + if (insts.size() > 0) { + Assert.isTrue((insts.get(0) instanceof Label), + "A method must begin with a Label, not " + insts.get(0)); + label = (Label) insts.get(0); + visited.add(label); + stack.push(new HeightRecord(label, 0)); + } + + // Also examine each exception handler. Recall that the exception + // object is implicitly pushed on the stack. So, the HeightRecord + // initially has height 1. + final Iterator e = method.tryCatches().iterator(); + while (e.hasNext()) { + final TryCatch tc = (TryCatch) e.next(); + visited.add(tc.handler()); + stack.push(new HeightRecord(tc.handler(), 1)); + } + + // Examine the HeightRecords on the stack. Make sure that the + // stack height has not exceeded 256. If the height at a given + // label has changed since we last visited it, then propagate this + // change to labels following the block begun by the label in + // question. + while (!stack.isEmpty()) { + final HeightRecord h = (HeightRecord) stack.pop(); + + Assert.isTrue(h.height < 256, "Stack height of " + h.height + + " reached. " + h.label + " (" + labelPos.get(h.label) + + ")"); + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(h.label + " has height " + h.height); + } + + Integer labelIndex = (Integer) labelPos.get(h.label); + Assert.isTrue(labelIndex != null, "Index of " + h.label + + " not found"); + + final int start = labelIndex.intValue(); + int diff = h.height - heights[start]; + + // Propagate the change in height to the next branch. + // Then push the branch targets. + if (ClassEditor.DEBUG) { + /* + * System.out.println(" " + h.label + ": change " + + * heights[start] + " to " + h.height); + */ + } + + heights[start] = h.height; + + final ListIterator blockIter = insts.listIterator(start + 1); + i = start; + + // Examine the instructions following the label + while (blockIter.hasNext()) { + final Object ce = blockIter.next(); + + i++; + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + if (inst.isReturn() || inst.isThrow()) { + // The method terminates. The stack is popped empty. + heights[i] = 0; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + + // Consider next HeightRecord on stack. + break; + + } else if (inst.isConditionalJump()) { + // If the stack height at this Label has changed since + // we + // last saw it or if we have not processed the target of + // the jump, add a new HeightRecord for the target + // Label. + + heights[i] += diff; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + + label = (Label) inst.operand(); + + if ((diff > 0) || !visited.contains(label)) { + visited.add(label); + stack.push(new HeightRecord(label, heights[i])); + } + + // Fall through. That is, process the instruction after + // the conditional jump. Remember that the code is in + // trace order so the false block (which is the next + // block + // in a depth first traversal) follows. The height of + // the + // stack won't change when we fall through. + + } else if (inst.isGoto() || inst.isJsr()) { + // Once again, if we have already visited the target + // block, add a HeightRecord to the stack. + + heights[i] += diff; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + + label = (Label) inst.operand(); + + if ((diff > 0) || !visited.contains(label)) { + visited.add(label); + stack.push(new HeightRecord(label, heights[i])); + } + + // Deal with the next HeightRecord on the stack. + break; + + } else if (inst.isRet()) { + // Process any unvisited return targets (of the current + // jsr) or those whose current height is less than the + // height at this return instruction. + + heights[i] += diff; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + + final Label subLabel = (Label) retInsts.get(inst); + Assert.isTrue(subLabel != null, + "Not inside a subroutine at " + inst); + + final Set targets = (Set) retTargets.get(subLabel); + Assert.isTrue(targets != null, "Subroutine " + subLabel + + " has no return targets"); + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" Returning from: " + + subLabel); + } + + final Iterator retIter = targets.iterator(); + + while (retIter.hasNext()) { + label = (Label) retIter.next(); + + labelIndex = (Integer) labelPos.get(label); + Assert.isTrue(labelIndex != null, "Index of " + + label + " not found"); + + final int idx = labelIndex.intValue(); + + if ((heights[idx] < heights[i]) + || !visited.contains(label)) { + visited.add(label); + stack.push(new HeightRecord(label, heights[i])); + } + } + + break; + + } else if (inst.isSwitch()) { + // Visit each unvisited switch target if it increases + // the + // stack height + + // If the height at this Label has changed since it was + // last visited, process each target Label. Otherwise, + // only process unvisited Labels. + + heights[i] += diff; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + + // A switch. + final Switch sw = (Switch) inst.operand(); + + label = sw.defaultTarget(); + + if ((diff > 0) || !visited.contains(label)) { + visited.add(label); + stack.push(new HeightRecord(label, heights[i])); + } + + final Label[] targets = sw.targets(); + + for (int j = 0; j < targets.length; j++) { + label = targets[j]; + if ((diff > 0) || !visited.contains(label)) { + visited.add(label); + stack.push(new HeightRecord(label, heights[i])); + } + } + + break; + + } else { + // No other blocks to visit. Just adjust the height. + + heights[i] += diff; + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + inst); + } + } + + } else if (ce instanceof Label) { + // We've hit the next block. Update the stack height. + // Process this next block if has not been visited or its + // current height is different from the previous + // instruction. + + label = (Label) ce; + + diff = heights[i - 1] - heights[i]; + + if ((diff > 0) || !visited.contains(label)) { + visited.add(label); + heights[i] = heights[i - 1]; + } + + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + heights[i] + ") " + label); + } + } + } + } + + // Find the maximum stack height. + maxStack = 0; + + for (i = 0; i < heights.length; i++) { + final int h = heights[i]; + + if (h > maxStack) { + maxStack = h; + } + } + } + + /** + * Returns the maximum number of local variables used by this method. + */ + public int maxLocals() { + return maxLocals; + } + + /** + * Returns the maximum height of the stack at any point in this method. + */ + public int maxStack() { + return maxStack; + } + + /** + * Returns the index in the byte array of the given label. + */ + public int labelIndex(final Label label) { + final Integer i = (Integer) labels.get(label); + + if (i != null) { + return i.intValue(); + } + + throw new IllegalArgumentException("Label " + label + " not found"); + } + + /** + * Returns the byte array after resolving branches. + */ + public byte[] array() { + if (branches.size() > 0) { + if (!longBranch && (codeLength >= 0x10000)) { + longBranch = true; + buildCode(); + } + } + + final byte[] c = new byte[codeLength]; + int i = codeLength; + + for (ByteCell p = codeTail; p != null; p = p.prev) { + c[--i] = p.value; + } + + Iterator e; + + e = branches.keySet().iterator(); + + while (e.hasNext()) { + final Integer branch = (Integer) e.next(); + final int branchIndex = branch.intValue(); + + final Integer inst = (Integer) branchInsts.get(branch); + final int instIndex = inst.intValue(); + + final Label label = (Label) branches.get(branch); + final Integer target = (Integer) labels.get(label); + + Assert.isTrue(target != null, "Index of " + label + " not found"); + + int diff = target.intValue() - instIndex; + + Assert.isTrue((-diff < 0x10000) && (diff < 0x10000), + "Branch offset too large: " + diff); + + c[branchIndex] = (byte) ((diff >>> 8) & 0xff); + c[branchIndex + 1] = (byte) (diff & 0xff); + } + + e = longBranches.keySet().iterator(); + + while (e.hasNext()) { + final Integer branch = (Integer) e.next(); + final int branchIndex = branch.intValue(); + + final Integer inst = (Integer) branchInsts.get(branch); + final int instIndex = inst.intValue(); + + final Label label = (Label) longBranches.get(branch); + final Integer target = (Integer) labels.get(label); + + final int diff = target.intValue() - instIndex; + + c[branchIndex] = (byte) ((diff >>> 24) & 0xff); + c[branchIndex + 1] = (byte) ((diff >>> 16) & 0xff); + c[branchIndex + 2] = (byte) ((diff >>> 8) & 0xff); + c[branchIndex + 3] = (byte) (diff & 0xff); + } + + return c; + } + + /** + * Makes note of a label. + */ + public void addLabel(final Label label) { + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + codeLength + ": " + "label " + label); + } + + labels.put(label, new Integer(codeLength)); + } + + /** + * Adds a 4-byte branch to a given label. The branch is from the index of + * the last opcode added. + */ + public void addLongBranch(final Label label) { + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + codeLength + ": " + "long branch to " + + label); + } + + branchInsts.put(new Integer(codeLength), new Integer(lastInst)); + longBranches.put(new Integer(codeLength), label); + addByte(0); + addByte(0); + addByte(0); + addByte(0); + } + + /** + * Adds a 2-byte branch to a given label. The branch is from the index of + * the last opcode added. + */ + public void addBranch(final Label label) { + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + codeLength + ": " + "branch to " + + label); + } + + branchInsts.put(new Integer(codeLength), new Integer(lastInst)); + branches.put(new Integer(codeLength), label); + addByte(0); + addByte(0); + } + + /** + * Add an opcode to the byte array, adjusting for 4-byte alignment for + * switch instructions and saving the index for calculating branches. + * + * @param opcode + * The opcode. + * @see Opcode + */ + public void addOpcode(final int opcode) { + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" " + codeLength + ": " + "opcode " + + Opcode.opcNames[opcode]); + } + + lastInst = codeLength; + + addByte(opcode); + + if ((opcode == Opcode.opc_tableswitch) + || (opcode == Opcode.opc_lookupswitch)) { + // Switch instructions are followed by padding so that table + // starts on a 4-byte boundary. + while (codeLength % 4 != 0) { + addByte(0); + } + } + } + + /** + * Adds a single byte to the array. + */ + public void addByte(final int i) { + if (ClassEditor.DEBUG) { + System.out.println(" " + codeLength + ": " + "byte " + i); + } + + // The bytecode array is represented as a linked list of + // ByteCells. This method creates a new ByteCell and appends it + // to the linked list. + + final ByteCell p = new ByteCell(); + p.value = (byte) (i & 0xff); + p.prev = codeTail; + codeTail = p; + codeLength++; + } + + /** + * Adds a 2-byte short to the array, high byte first. + */ + public void addShort(final int i) { + if (ClassEditor.DEBUG) { + System.out.println(" " + codeLength + ": " + "short " + i); + } + + addByte(i >>> 8); + addByte(i); + } + + /** + * Adds a 4-byte int to the array, high byte first. + */ + public void addInt(final int i) { + if (ClassEditor.DEBUG) { + System.out.println(" " + codeLength + ": " + "int " + i); + } + + addByte(i >>> 24); + addByte(i >>> 16); + addByte(i >>> 8); + addByte(i); + } + + public void visit_nop(final Instruction inst) { + // If it must have been put there for a reason. + addOpcode(Opcode.opc_nop); + stackHeight += 0; + } + + /* + * Does pretty much what you'd expect. Examines the instruction's operand to + * determine if one of the special constant opcodes (e.g. iconst_1) can be + * used. Adds the most appropriate instruction. + */ + public void visit_ldc(final Instruction inst) { + final Object operand = inst.operand(); + + if (operand == null) { + addOpcode(Opcode.opc_aconst_null); + stackHeight++; + + } else if (operand instanceof Integer) { + final int v = ((Integer) operand).intValue(); + + switch (v) { + case -1: + addOpcode(Opcode.opc_iconst_m1); + break; + case 0: + addOpcode(Opcode.opc_iconst_0); + break; + case 1: + addOpcode(Opcode.opc_iconst_1); + break; + case 2: + addOpcode(Opcode.opc_iconst_2); + break; + case 3: + addOpcode(Opcode.opc_iconst_3); + break; + case 4: + addOpcode(Opcode.opc_iconst_4); + break; + case 5: + addOpcode(Opcode.opc_iconst_5); + break; + default: { + if ((byte) v == v) { + addOpcode(Opcode.opc_bipush); + addByte(v); + } else if ((short) v == v) { + addOpcode(Opcode.opc_sipush); + addShort(v); + } else { + final int index = constants.addConstant(Constant.INTEGER, + operand); + if (index < 256) { + addOpcode(Opcode.opc_ldc); + addByte(index); + } else { + addOpcode(Opcode.opc_ldc_w); + addShort(index); + } + } + break; + } + } + + stackHeight++; + + } else if (operand instanceof Float) { + final float v = ((Float) operand).floatValue(); + + if (v == 0.0F) { + addOpcode(Opcode.opc_fconst_0); + } else if (v == 1.0F) { + addOpcode(Opcode.opc_fconst_1); + } else if (v == 2.0F) { + addOpcode(Opcode.opc_fconst_2); + } else { + final int index = constants + .addConstant(Constant.FLOAT, operand); + if (index < 256) { + addOpcode(Opcode.opc_ldc); + addByte(index); + } else { + addOpcode(Opcode.opc_ldc_w); + addShort(index); + } + } + + stackHeight++; + + } else if (operand instanceof Long) { + final long v = ((Long) operand).longValue(); + + if (v == 0) { + addOpcode(Opcode.opc_lconst_0); + } else if (v == 1) { + addOpcode(Opcode.opc_lconst_1); + } else { + final int index = constants.addConstant(Constant.LONG, operand); + addOpcode(Opcode.opc_ldc2_w); + addShort(index); + } + + stackHeight += 2; + + } else if (operand instanceof Double) { + final double v = ((Double) operand).doubleValue(); + + if (v == 0.0) { + addOpcode(Opcode.opc_dconst_0); + } else if (v == 1.0) { + addOpcode(Opcode.opc_dconst_1); + } else { + final int index = constants.addConstant(Constant.DOUBLE, + operand); + addOpcode(Opcode.opc_ldc2_w); + addShort(index); + } + + stackHeight += 2; + + } else if (operand instanceof String) { + final int index = constants.addConstant(Constant.STRING, operand); + + if (index < 256) { + addOpcode(Opcode.opc_ldc); + addByte(index); + } else { + addOpcode(Opcode.opc_ldc_w); + addShort(index); + } + + stackHeight++; + } else { + throw new RuntimeException(); + } + } + + /* + * Tries to use the shorter iload_x instructions. + */ + public void visit_iload(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_iload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_iload); + addShort(index); + } + stackHeight++; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_iload_0); + break; + case 1: + addOpcode(Opcode.opc_iload_1); + break; + case 2: + addOpcode(Opcode.opc_iload_2); + break; + case 3: + addOpcode(Opcode.opc_iload_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_iload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_iload); + addShort(index); + } + break; + } + + stackHeight++; + } + + public void visit_lload(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 2 > maxLocals) { + maxLocals = index + 2; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_lload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_lload); + addShort(index); + } + stackHeight++; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_lload_0); + break; + case 1: + addOpcode(Opcode.opc_lload_1); + break; + case 2: + addOpcode(Opcode.opc_lload_2); + break; + case 3: + addOpcode(Opcode.opc_lload_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_lload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_lload); + addShort(index); + } + break; + } + + stackHeight += 2; + } + + public void visit_fload(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_fload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_fload); + addShort(index); + } + + stackHeight++; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_fload_0); + break; + case 1: + addOpcode(Opcode.opc_fload_1); + break; + case 2: + addOpcode(Opcode.opc_fload_2); + break; + case 3: + addOpcode(Opcode.opc_fload_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_fload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_fload); + addShort(index); + } + break; + } + + stackHeight++; + } + + public void visit_dload(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 2 > maxLocals) { + maxLocals = index + 2; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_dload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_dload); + addShort(index); + } + stackHeight += 2; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_dload_0); + break; + case 1: + addOpcode(Opcode.opc_dload_1); + break; + case 2: + addOpcode(Opcode.opc_dload_2); + break; + case 3: + addOpcode(Opcode.opc_dload_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_dload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_dload); + addShort(index); + } + break; + } + + stackHeight += 2; + } + + public void visit_aload(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_aload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_aload); + addShort(index); + } + stackHeight++; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_aload_0); + break; + case 1: + addOpcode(Opcode.opc_aload_1); + break; + case 2: + addOpcode(Opcode.opc_aload_2); + break; + case 3: + addOpcode(Opcode.opc_aload_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_aload); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_aload); + addShort(index); + } + break; + } + + stackHeight++; + } + + /** + * Pops an item off the stack. + */ + public void visit_iaload(final Instruction inst) { + addOpcode(Opcode.opc_iaload); + stackHeight--; + } + + public void visit_laload(final Instruction inst) { + addOpcode(Opcode.opc_laload); + stackHeight += 0; + } + + public void visit_faload(final Instruction inst) { + addOpcode(Opcode.opc_faload); + stackHeight--; + } + + public void visit_daload(final Instruction inst) { + addOpcode(Opcode.opc_daload); + stackHeight += 0; + } + + public void visit_aaload(final Instruction inst) { + addOpcode(Opcode.opc_aaload); + stackHeight--; + } + + public void visit_baload(final Instruction inst) { + addOpcode(Opcode.opc_baload); + stackHeight--; + } + + public void visit_caload(final Instruction inst) { + addOpcode(Opcode.opc_caload); + stackHeight--; + } + + public void visit_saload(final Instruction inst) { + addOpcode(Opcode.opc_saload); + stackHeight--; + } + + /* + * Try to take advantage of smaller opcodes (e.g. istore_1). + */ + public void visit_istore(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_istore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_istore); + addShort(index); + } + stackHeight--; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_istore_0); + break; + case 1: + addOpcode(Opcode.opc_istore_1); + break; + case 2: + addOpcode(Opcode.opc_istore_2); + break; + case 3: + addOpcode(Opcode.opc_istore_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_istore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_istore); + addShort(index); + } + break; + } + + stackHeight--; + } + + public void visit_lstore(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 2 > maxLocals) { + maxLocals = index + 2; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_lstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_lstore); + addShort(index); + } + stackHeight -= 2; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_lstore_0); + break; + case 1: + addOpcode(Opcode.opc_lstore_1); + break; + case 2: + addOpcode(Opcode.opc_lstore_2); + break; + case 3: + addOpcode(Opcode.opc_lstore_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_lstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_lstore); + addShort(index); + } + break; + } + + stackHeight -= 2; + } + + public void visit_fstore(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_fstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_fstore); + addShort(index); + } + stackHeight--; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_fstore_0); + break; + case 1: + addOpcode(Opcode.opc_fstore_1); + break; + case 2: + addOpcode(Opcode.opc_fstore_2); + break; + case 3: + addOpcode(Opcode.opc_fstore_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_fstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_fstore); + addShort(index); + } + break; + } + + stackHeight--; + } + + public void visit_dstore(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 2 > maxLocals) { + maxLocals = index + 2; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_dstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_dstore); + addShort(index); + } + stackHeight -= 2; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_dstore_0); + break; + case 1: + addOpcode(Opcode.opc_dstore_1); + break; + case 2: + addOpcode(Opcode.opc_dstore_2); + break; + case 3: + addOpcode(Opcode.opc_dstore_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_dstore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_dstore); + addShort(index); + } + break; + } + + stackHeight -= 2; + } + + public void visit_astore(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (inst.useSlow()) { + if (index < 256) { + addOpcode(Opcode.opc_astore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_astore); + addShort(index); + } + stackHeight--; + return; + } + + switch (index) { + case 0: + addOpcode(Opcode.opc_astore_0); + break; + case 1: + addOpcode(Opcode.opc_astore_1); + break; + case 2: + addOpcode(Opcode.opc_astore_2); + break; + case 3: + addOpcode(Opcode.opc_astore_3); + break; + default: + if (index < 256) { + addOpcode(Opcode.opc_astore); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_astore); + addShort(index); + } + break; + } + + stackHeight--; + } + + /* + * Store into an array. Pop 3+ items off the stack. + */ + public void visit_iastore(final Instruction inst) { + addOpcode(Opcode.opc_iastore); + stackHeight -= 3; + } + + public void visit_lastore(final Instruction inst) { + addOpcode(Opcode.opc_lastore); + stackHeight -= 4; + } + + public void visit_fastore(final Instruction inst) { + addOpcode(Opcode.opc_fastore); + stackHeight -= 3; + } + + public void visit_dastore(final Instruction inst) { + addOpcode(Opcode.opc_dastore); + stackHeight -= 4; + } + + public void visit_aastore(final Instruction inst) { + addOpcode(Opcode.opc_aastore); + stackHeight -= 3; + } + + public void visit_bastore(final Instruction inst) { + addOpcode(Opcode.opc_bastore); + stackHeight -= 3; + } + + public void visit_castore(final Instruction inst) { + addOpcode(Opcode.opc_castore); + stackHeight -= 3; + } + + public void visit_sastore(final Instruction inst) { + addOpcode(Opcode.opc_sastore); + stackHeight -= 3; + } + + public void visit_pop(final Instruction inst) { + addOpcode(Opcode.opc_pop); + stackHeight--; + } + + public void visit_pop2(final Instruction inst) { + addOpcode(Opcode.opc_pop2); + stackHeight -= 2; + } + + public void visit_dup(final Instruction inst) { + addOpcode(Opcode.opc_dup); + stackHeight++; + } + + public void visit_dup_x1(final Instruction inst) { + addOpcode(Opcode.opc_dup_x1); + stackHeight++; + } + + public void visit_dup_x2(final Instruction inst) { + addOpcode(Opcode.opc_dup_x2); + stackHeight++; + } + + public void visit_dup2(final Instruction inst) { + addOpcode(Opcode.opc_dup2); + stackHeight += 2; + } + + public void visit_dup2_x1(final Instruction inst) { + addOpcode(Opcode.opc_dup2_x1); + stackHeight += 2; + } + + public void visit_dup2_x2(final Instruction inst) { + addOpcode(Opcode.opc_dup2_x2); + stackHeight += 2; + } + + public void visit_swap(final Instruction inst) { + addOpcode(Opcode.opc_swap); + } + + public void visit_iadd(final Instruction inst) { + addOpcode(Opcode.opc_iadd); + stackHeight--; + } + + public void visit_ladd(final Instruction inst) { + addOpcode(Opcode.opc_ladd); + stackHeight -= 2; + } + + public void visit_fadd(final Instruction inst) { + addOpcode(Opcode.opc_fadd); + stackHeight--; + } + + public void visit_dadd(final Instruction inst) { + addOpcode(Opcode.opc_dadd); + stackHeight -= 2; + } + + public void visit_isub(final Instruction inst) { + addOpcode(Opcode.opc_isub); + stackHeight--; + } + + public void visit_lsub(final Instruction inst) { + addOpcode(Opcode.opc_lsub); + stackHeight -= 2; + } + + public void visit_fsub(final Instruction inst) { + addOpcode(Opcode.opc_fsub); + stackHeight--; + } + + public void visit_dsub(final Instruction inst) { + addOpcode(Opcode.opc_dsub); + stackHeight -= 2; + } + + public void visit_imul(final Instruction inst) { + addOpcode(Opcode.opc_imul); + stackHeight--; + } + + public void visit_lmul(final Instruction inst) { + addOpcode(Opcode.opc_lmul); + stackHeight -= 2; + } + + public void visit_fmul(final Instruction inst) { + addOpcode(Opcode.opc_fmul); + stackHeight--; + } + + public void visit_dmul(final Instruction inst) { + addOpcode(Opcode.opc_dmul); + stackHeight -= 2; + } + + public void visit_idiv(final Instruction inst) { + addOpcode(Opcode.opc_idiv); + stackHeight--; + } + + public void visit_ldiv(final Instruction inst) { + addOpcode(Opcode.opc_ldiv); + stackHeight -= 2; + } + + public void visit_fdiv(final Instruction inst) { + addOpcode(Opcode.opc_fdiv); + stackHeight--; + } + + public void visit_ddiv(final Instruction inst) { + addOpcode(Opcode.opc_ddiv); + stackHeight -= 2; + } + + public void visit_irem(final Instruction inst) { + addOpcode(Opcode.opc_irem); + stackHeight--; + } + + public void visit_lrem(final Instruction inst) { + addOpcode(Opcode.opc_lrem); + stackHeight -= 2; + } + + public void visit_frem(final Instruction inst) { + addOpcode(Opcode.opc_frem); + stackHeight--; + } + + public void visit_drem(final Instruction inst) { + addOpcode(Opcode.opc_drem); + stackHeight -= 2; + } + + public void visit_ineg(final Instruction inst) { + addOpcode(Opcode.opc_ineg); + stackHeight += 0; + } + + public void visit_lneg(final Instruction inst) { + addOpcode(Opcode.opc_lneg); + stackHeight += 0; + } + + public void visit_fneg(final Instruction inst) { + addOpcode(Opcode.opc_fneg); + stackHeight += 0; + } + + public void visit_dneg(final Instruction inst) { + addOpcode(Opcode.opc_dneg); + stackHeight += 0; + } + + public void visit_ishl(final Instruction inst) { + addOpcode(Opcode.opc_ishl); + stackHeight--; + } + + public void visit_lshl(final Instruction inst) { + addOpcode(Opcode.opc_lshl); + stackHeight--; + } + + public void visit_ishr(final Instruction inst) { + addOpcode(Opcode.opc_ishr); + stackHeight--; + } + + public void visit_lshr(final Instruction inst) { + addOpcode(Opcode.opc_lshr); + stackHeight--; + } + + public void visit_iushr(final Instruction inst) { + addOpcode(Opcode.opc_iushr); + stackHeight--; + } + + public void visit_lushr(final Instruction inst) { + addOpcode(Opcode.opc_lushr); + stackHeight--; + } + + public void visit_iand(final Instruction inst) { + addOpcode(Opcode.opc_iand); + stackHeight--; + } + + public void visit_land(final Instruction inst) { + addOpcode(Opcode.opc_land); + stackHeight -= 2; + } + + public void visit_ior(final Instruction inst) { + addOpcode(Opcode.opc_ior); + stackHeight--; + } + + public void visit_lor(final Instruction inst) { + addOpcode(Opcode.opc_lor); + stackHeight -= 2; + } + + public void visit_ixor(final Instruction inst) { + addOpcode(Opcode.opc_ixor); + stackHeight--; + } + + public void visit_lxor(final Instruction inst) { + addOpcode(Opcode.opc_lxor); + stackHeight -= 2; + } + + public void visit_iinc(final Instruction inst) { + final IncOperand operand = (IncOperand) inst.operand(); + + final int index = operand.var().index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + final int incr = operand.incr(); + + if ((index < 256) && ((byte) incr == incr)) { + addOpcode(Opcode.opc_iinc); + addByte(index); + addByte(incr); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_iinc); + addShort(index); + addShort(incr); + } + + stackHeight += 0; + } + + public void visit_i2l(final Instruction inst) { + addOpcode(Opcode.opc_i2l); + stackHeight++; + } + + public void visit_i2f(final Instruction inst) { + addOpcode(Opcode.opc_i2f); + stackHeight += 0; + } + + public void visit_i2d(final Instruction inst) { + addOpcode(Opcode.opc_i2d); + stackHeight++; + } + + public void visit_l2i(final Instruction inst) { + addOpcode(Opcode.opc_l2i); + stackHeight--; + } + + public void visit_l2f(final Instruction inst) { + addOpcode(Opcode.opc_l2f); + stackHeight--; + } + + public void visit_l2d(final Instruction inst) { + addOpcode(Opcode.opc_l2d); + stackHeight += 0; + } + + public void visit_f2i(final Instruction inst) { + addOpcode(Opcode.opc_f2i); + stackHeight += 0; + } + + public void visit_f2l(final Instruction inst) { + addOpcode(Opcode.opc_f2l); + stackHeight++; + } + + public void visit_f2d(final Instruction inst) { + addOpcode(Opcode.opc_f2d); + stackHeight++; + } + + public void visit_d2i(final Instruction inst) { + addOpcode(Opcode.opc_d2i); + stackHeight--; + } + + public void visit_d2l(final Instruction inst) { + addOpcode(Opcode.opc_d2l); + stackHeight += 0; + } + + public void visit_d2f(final Instruction inst) { + addOpcode(Opcode.opc_d2f); + stackHeight--; + } + + public void visit_i2b(final Instruction inst) { + addOpcode(Opcode.opc_i2b); + stackHeight += 0; + } + + public void visit_i2c(final Instruction inst) { + addOpcode(Opcode.opc_i2c); + stackHeight += 0; + } + + public void visit_i2s(final Instruction inst) { + addOpcode(Opcode.opc_i2s); + stackHeight += 0; + } + + public void visit_lcmp(final Instruction inst) { + addOpcode(Opcode.opc_lcmp); + stackHeight -= 3; + } + + public void visit_fcmpl(final Instruction inst) { + addOpcode(Opcode.opc_fcmpl); + stackHeight--; + } + + public void visit_fcmpg(final Instruction inst) { + addOpcode(Opcode.opc_fcmpg); + stackHeight--; + } + + public void visit_dcmpl(final Instruction inst) { + addOpcode(Opcode.opc_dcmpl); + stackHeight -= 3; + } + + public void visit_dcmpg(final Instruction inst) { + addOpcode(Opcode.opc_dcmpg); + stackHeight -= 3; + } + + /* + * Handle long branches. + */ + public void visit_ifeq(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifne); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifeq); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_ifne(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifeq); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifne); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_iflt(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifge); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_iflt); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_ifge(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_iflt); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifge); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_ifgt(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifle); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifgt); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_ifle(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifgt); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifle); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_if_icmpeq(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmpne); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmpeq); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_icmpne(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmpeq); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmpne); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_icmplt(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmpge); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmplt); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_icmpge(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmplt); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmpge); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_icmpgt(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmple); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmpgt); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_icmple(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_icmpgt); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_icmple); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_acmpeq(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_acmpne); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_acmpeq); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_if_acmpne(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_if_acmpeq); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_if_acmpne); + addBranch((Label) inst.operand()); + } + + stackHeight -= 2; + } + + public void visit_goto(final Instruction inst) { + if (longBranch) { + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + } else { + addOpcode(Opcode.opc_goto); + addBranch((Label) inst.operand()); + } + + stackHeight += 0; + } + + public void visit_jsr(final Instruction inst) { + if (longBranch) { + addOpcode(Opcode.opc_jsr_w); + addLongBranch((Label) inst.operand()); + } else { + addOpcode(Opcode.opc_jsr); + addBranch((Label) inst.operand()); + } + + stackHeight++; + } + + public void visit_ret(final Instruction inst) { + final int index = ((LocalVariable) inst.operand()).index(); + + if (index + 1 > maxLocals) { + maxLocals = index + 1; + } + + if (index < 256) { + addOpcode(Opcode.opc_ret); + addByte(index); + } else { + addOpcode(Opcode.opc_wide); + addByte(Opcode.opc_ret); + addShort(index); + } + + stackHeight += 0; + } + + public void visit_switch(final Instruction inst) { + final Switch sw = (Switch) inst.operand(); + + final int[] values = sw.values(); + final Label[] targets = sw.targets(); + + if (values.length == 0) { + if (longBranch) { + addOpcode(Opcode.opc_pop); // Pop switch "index" off stack + addOpcode(Opcode.opc_goto_w); + addLongBranch(sw.defaultTarget()); + } else { + addOpcode(Opcode.opc_pop); // Pop switch "index" off stack + addOpcode(Opcode.opc_goto); + addBranch(sw.defaultTarget()); + } + } else if (sw.hasContiguousValues()) { + addOpcode(Opcode.opc_tableswitch); + addLongBranch(sw.defaultTarget()); + + addInt(values[0]); + addInt(values[values.length - 1]); + + for (int i = 0; i < targets.length; i++) { + addLongBranch(targets[i]); + } + } else { + addOpcode(Opcode.opc_lookupswitch); + addLongBranch(sw.defaultTarget()); + + addInt(values.length); + + for (int i = 0; i < targets.length; i++) { + addInt(values[i]); + addLongBranch(targets[i]); + } + } + + stackHeight--; + } + + public void visit_ireturn(final Instruction inst) { + addOpcode(Opcode.opc_ireturn); + stackHeight = 0; + } + + public void visit_lreturn(final Instruction inst) { + addOpcode(Opcode.opc_lreturn); + stackHeight = 0; + } + + public void visit_freturn(final Instruction inst) { + addOpcode(Opcode.opc_freturn); + stackHeight = 0; + } + + public void visit_dreturn(final Instruction inst) { + addOpcode(Opcode.opc_dreturn); + stackHeight = 0; + } + + public void visit_areturn(final Instruction inst) { + addOpcode(Opcode.opc_areturn); + stackHeight = 0; + } + + public void visit_return(final Instruction inst) { + addOpcode(Opcode.opc_return); + stackHeight = 0; + } + + public void visit_getstatic(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_getstatic); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight += type.stackHeight(); + } + + public void visit_putstatic(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_putstatic); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight(); + } + + public void visit_putstatic_nowb(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_putstatic_nowb); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight(); + } + + public void visit_getfield(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_getfield); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight += type.stackHeight() - 1; + } + + public void visit_putfield(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_putfield); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight() + 1; + } + + public void visit_putfield_nowb(final Instruction inst) { + final int index = constants.addConstant(Constant.FIELD_REF, inst + .operand()); + addOpcode(Opcode.opc_putfield_nowb); + addShort(index); + + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight() + 1; + } + + public void visit_invokevirtual(final Instruction inst) { + final int index = constants.addConstant(Constant.METHOD_REF, inst + .operand()); + addOpcode(Opcode.opc_invokevirtual); + addShort(index); + + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1; + } + + public void visit_invokespecial(final Instruction inst) { + final int index = constants.addConstant(Constant.METHOD_REF, inst + .operand()); + addOpcode(Opcode.opc_invokespecial); + addShort(index); + + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1; + } + + public void visit_invokestatic(final Instruction inst) { + final int index = constants.addConstant(Constant.METHOD_REF, inst + .operand()); + addOpcode(Opcode.opc_invokestatic); + addShort(index); + + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + Assert.isTrue(type.isMethod(), + "Trying to invoke a type that is not a method: " + method); + + stackHeight += type.returnType().stackHeight() - type.stackHeight(); + } + + public void visit_invokeinterface(final Instruction inst) { + final int index = constants.addConstant(Constant.INTERFACE_METHOD_REF, + inst.operand()); + final MemberRef method = (MemberRef) constants.constantAt(index); + final Type type = method.nameAndType().type(); + + addOpcode(Opcode.opc_invokeinterface); + addShort(index); + addByte(type.stackHeight() + 1); + addByte(0); + + stackHeight += type.returnType().stackHeight() - type.stackHeight() - 1; + } + + public void visit_new(final Instruction inst) { + final int index = constants.addConstant(Constant.CLASS, inst.operand()); + addOpcode(Opcode.opc_new); + addShort(index); + + stackHeight++; + } + + public void visit_newarray(final Instruction inst) { + final Type type = (Type) inst.operand(); + + if (type.isReference()) { + final int index = constants.addConstant(Constant.CLASS, type); + addOpcode(Opcode.opc_anewarray); + addShort(index); + } else { + addOpcode(Opcode.opc_newarray); + addByte(type.typeCode()); + } + + stackHeight += 0; + } + + public void visit_arraylength(final Instruction inst) { + addOpcode(Opcode.opc_arraylength); + stackHeight += 0; + } + + public void visit_athrow(final Instruction inst) { + addOpcode(Opcode.opc_athrow); + stackHeight = 0; + } + + public void visit_checkcast(final Instruction inst) { + final int index = constants.addConstant(Constant.CLASS, inst.operand()); + addOpcode(Opcode.opc_checkcast); + addShort(index); + stackHeight += 0; + } + + public void visit_instanceof(final Instruction inst) { + final int index = constants.addConstant(Constant.CLASS, inst.operand()); + addOpcode(Opcode.opc_instanceof); + addShort(index); + stackHeight += 0; + } + + public void visit_monitorenter(final Instruction inst) { + addOpcode(Opcode.opc_monitorenter); + stackHeight--; + } + + public void visit_monitorexit(final Instruction inst) { + addOpcode(Opcode.opc_monitorexit); + stackHeight--; + } + + public void visit_multianewarray(final Instruction inst) { + final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); + final Type type = operand.type(); + final int dim = operand.dimensions(); + final int index = constants.addConstant(Constant.CLASS, type); + addOpcode(Opcode.opc_multianewarray); + addShort(index); + addByte(dim); + + stackHeight += 1 - dim; + } + + public void visit_ifnull(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifnonnull); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifnull); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_ifnonnull(final Instruction inst) { + if (longBranch) { + final Label tmp = method.newLabel(); + addOpcode(Opcode.opc_ifnull); + addBranch(tmp); + addOpcode(Opcode.opc_goto_w); + addLongBranch((Label) inst.operand()); + addLabel(tmp); + } else { + addOpcode(Opcode.opc_ifnonnull); + addBranch((Label) inst.operand()); + } + + stackHeight--; + } + + public void visit_rc(final Instruction inst) { + final Integer operand = (Integer) inst.operand(); + addOpcode(Opcode.opc_rc); + addByte(operand.intValue()); + stackHeight += 0; + } + + public void visit_aswizzle(final Instruction inst) { + addOpcode(Opcode.opc_aswizzle); + stackHeight -= 2; + } + + public void visit_aswrange(final Instruction inst) { + addOpcode(Opcode.opc_aswrange); + stackHeight -= 3; + } + + public void visit_aupdate(final Instruction inst) { + final Integer operand = (Integer) inst.operand(); + addOpcode(Opcode.opc_aupdate); + addByte(operand.intValue()); + stackHeight += 0; + } + + public void visit_supdate(final Instruction inst) { + final Integer operand = (Integer) inst.operand(); + addOpcode(Opcode.opc_supdate); + addByte(operand.intValue()); + stackHeight += 0; + } + + /** + * Represents the height of the stack at given Label. + */ + class HeightRecord { + Label label; + + int height; + + public HeightRecord(final Label label, final int height) { + if (ClassEditor.DEBUG || CodeArray.DEBUG) { + System.out.println(" push " + label + " at " + height); + } + + this.label = label; + this.height = height; + } + } + + /** + * Used to represent the byte array. + */ + class ByteCell { + byte value; + + ByteCell prev; + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/ConstantPool.java b/src/EDU/purdue/cs/bloat/editor/ConstantPool.java new file mode 100644 index 0000000..9fc295f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/ConstantPool.java @@ -0,0 +1,459 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * ConstantPool models constants in the constant pool. Recall that the + * reflection mechanism represents constants as a tag and a value. ConstantPool + * consists of an array of reflect.Constants that are resolved into + * their appropriate value (e.g. Type, NameAndType, MemberRef, etc.) as they are + * needed. + * + * @see EDU.purdue.cs.bloat.reflect.Constant + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class ConstantPool { + /** + * ConstantPool maintains some information about the constants in the + * constant pool to make its life easier. + * + * constantIndices A mapping between constants and their indices in the + * constant pool. Knowing this information makes adding constants to the + * constant pool easier. + * + * constants A list of the reflect.Constant objects that are used to create + * the ConstantPool. + * + * resolved A list of the constants in their resolved form (such as + * NameAndType or String) + */ + private Map constantIndices; + + ResizeableArrayList constants; + + ResizeableArrayList resolved; + + /** + * Constructor. Resolve the constants in the constant pool, converting from + * the index-based representation of the class file to a more amenable + * external representation. + * + * @param c + * An array of Constant. + */ + public ConstantPool(final Constant[] c) { + constantIndices = new HashMap(); + constants = new ResizeableArrayList(c.length); + resolved = new ResizeableArrayList(c.length); + + /* + * constants = new ResizeableArrayList(c.length, 256); resolved = new + * ResizeableArrayList(c.length, 256); + */ + for (int i = 0; i < c.length; i++) { + constants.add(c[i]); + resolved.add(null); + if (c[i] != null) { + constantIndices.put(c[i], new Integer(i)); + } + } + } + + /** + * Creates a new, empty ConstantPool + */ + public ConstantPool() { + this.constantIndices = new HashMap(); + this.constants = new ResizeableArrayList(); + this.resolved = new ResizeableArrayList(); + } + + /** + * Obtain the resolved value of a constant at given index in the constant + * pool. + * + * @param idx + * Index into the constant pool. + * @return The value of the constant at index. + */ + public Object constantAt(final int idx) { + if (idx == 0) { + return null; + } + + Object value = resolved.get(idx); + + if (value != null) { + return value; + } + + final Constant c = (Constant) constants.get(idx); + + if (c == null) { + return null; + } + + value = c.value(); + + if (value == null) { + return null; + } + + // Okay, we have to resolve the Constant. + + switch (c.tag()) { + case Constant.CLASS: { + // Lookup the UTF8 at the index and use the class name + // to create a Type. + Assert.isTrue(value instanceof Integer, "Invalid constant: " + c); + + final int index = ((Integer) value).intValue(); + Assert.isTrue(constantTag(index) == Constant.UTF8, + "Invalid constant: " + c); + + final String className = (String) constantAt(index); + + value = Type.getType(Type.classDescriptor(className)); + break; + } + case Constant.STRING: { + // Lookup the UTF8 at the index and store the String directly. + Assert.isTrue(value instanceof Integer, "Invalid constant: " + c); + + final int index = ((Integer) value).intValue(); + Assert.isTrue(constantTag(index) == Constant.UTF8, + "Invalid constant: " + c); + + value = constantAt(index); + break; + } + case Constant.FIELD_REF: + case Constant.METHOD_REF: + case Constant.INTERFACE_METHOD_REF: { + // The constant at the first index should be a CLASS. + // + // The constant at the second should be a NAME_AND_TYPE. + // + // Resolve the two constants and then create a MemberRef + // for this constant. + // + Assert.isTrue(value instanceof int[], "Invalid constant: " + c); + + final int[] v = (int[]) value; + + Assert.isTrue(constantTag(v[0]) == Constant.CLASS, + "Invalid constant: " + c); + Assert.isTrue(constantTag(v[1]) == Constant.NAME_AND_TYPE, + "Invalid constant: " + c); + + final Type clazz = (Type) constantAt(v[0]); + final NameAndType nameAndType = (NameAndType) constantAt(v[1]); + + value = new MemberRef(clazz, nameAndType); + break; + } + case Constant.NAME_AND_TYPE: { + // The constant at the first index should be a UTF8 with the + // name of the field. + // + // The constant at the second should be a UTF8 with the type + // of the field. + // + // Resolve the two constants as a String and a Type and then + // create a NameAndType for this constant. + // + Assert.isTrue(value instanceof int[], "Invalid constant: " + c); + + final int[] v = (int[]) value; + + Assert.isTrue(constantTag(v[0]) == Constant.UTF8, + "Invalid constant: " + c); + Assert.isTrue(constantTag(v[1]) == Constant.UTF8, + "Invalid constant: " + c); + + final String name = (String) constantAt(v[0]); + final String type = (String) constantAt(v[1]); + + value = new NameAndType(name, Type.getType(type)); + break; + } + default: + break; + } + + resolved.ensureSize(idx + 1); + resolved.set(idx, value); + + return value; + } + + public int numConstants() { + return constants.size(); + } + + /** + * Get the tag of a constant. + * + * @param index + * Index into the constant pool. + * @return The tag of the constant at index, or Constant.UTF8. + */ + public int constantTag(final int index) { + if ((0 < index) && (index < constants.size())) { + final Constant c = (Constant) constants.get(index); + + if (c != null) { + return c.tag(); + } + } + + return Constant.UTF8; + } + + /** + * Get the index of the constant with the given tag and value. + * + * @param tag + * The constant's tag (for example, Constant.UTF8). + * @param value + * The constant's value (for example, a String). + * @return The index of the constant. + */ + public int constantIndex(final int tag, final Object value) { + return addConstant(tag, value); + } + + /** + * Returns the index of the constant pool entry for the given class + */ + public int getClassIndex(final Class c) { + return (addConstant(Constant.CLASS, Type.getType(c))); + } + + /** + * Returns the index of the constant pool entry for the given integer + */ + public int getIntegerIndex(final Integer i) { + return (addConstant(Constant.INTEGER, i)); + } + + /** + * Returns the index of the constant pool entry for the given float + */ + public int getFloatIndex(final Float f) { + return (addConstant(Constant.FLOAT, f)); + } + + /** + * Returns the index of the constant pool entry for the given long + */ + public int getLongIndex(final Long l) { + return (addConstant(Constant.LONG, l)); + } + + /** + * Returns the index of the constant pool entry for the given double + */ + public int getDoubleIndex(final Double d) { + return (addConstant(Constant.DOUBLE, d)); + } + + /** + * Returns the index of the constant pool entry for the given class. + */ + public int getClassIndex(final Type type) { + Assert + .isTrue(type.isObject(), "Type " + type + + " is not an class type"); + // Make sure that the descriptor constant is also there + getTypeIndex(type); + return (addConstant(Constant.CLASS, type)); + } + + /** + * Returns the index of the constant pool entry for the given + * Type + */ + public int getTypeIndex(final Type type) { + return (addConstant(Constant.UTF8, type.descriptor())); + } + + /** + * Returns the index of the constant pool entry for the given String + */ + public int getStringIndex(final String s) { + return (addConstant(Constant.STRING, s)); + } + + /** + * Returns the index of the constant pool entry for the given + * MemberRef + */ + public int getMemberRefIndex(final MemberRef ref) { + return (addConstant(Constant.FIELD_REF, ref)); + } + + /** + * Returns the index of the constant pool entry for the given + * NameAndType + */ + public int getNameAndTypeIndex(final NameAndType nat) { + return (addConstant(Constant.NAME_AND_TYPE, nat)); + } + + /** + * Returns the index of the constant pool entry for the given UTF8 string + */ + public int getUTF8Index(final String s) { + return (addConstant(Constant.UTF8, s)); + } + + /** + * Add a constant to the constant pool. Will not add the same constant + * twice. + * + * @param tag + * The constant's tag (for example, Constant.UTF8). + * @param value + * The constant's value (for example, a String). + * @return The index of the constant. + */ + public int addConstant(final int tag, final Object value) { + if (value == null) { + return 0; + } + + Constant c; + + switch (tag) { + case Constant.CLASS: { + Assert.isTrue(value instanceof Type, "Invalid value: " + value); + final int index = addConstant(Constant.UTF8, ((Type) value) + .className()); + c = new Constant(Constant.CLASS, new Integer(index)); + break; + } + case Constant.STRING: { + Assert.isTrue(value instanceof String, "Invalid value: " + value); + final int index = addConstant(Constant.UTF8, value); + c = new Constant(Constant.STRING, new Integer(index)); + break; + } + case Constant.FIELD_REF: + case Constant.METHOD_REF: + case Constant.INTERFACE_METHOD_REF: { + // The constant at the first index should be a CLASS. + // + // The constant at the second should be a NAME_AND_TYPE. + // + // Resolve the two constants and then create a MemberRef + // for this constant. + // + Assert + .isTrue(value instanceof MemberRef, "Invalid value: " + + value); + + final int[] v = new int[2]; + + v[0] = addConstant(Constant.CLASS, ((MemberRef) value) + .declaringClass()); + v[1] = addConstant(Constant.NAME_AND_TYPE, ((MemberRef) value) + .nameAndType()); + + c = new Constant(tag, v); + break; + } + case Constant.NAME_AND_TYPE: { + // The constant at the first index should be a UTF8 with the + // name of the field. + // + // The constant at the second should be a UTF8 with the type + // of the field. + // + // Resolve the two constants as a String and a Type and then + // create a NameAndType for this constant. + // + Assert.isTrue(value instanceof NameAndType, "Invalid value: " + + value); + + final int[] v = new int[2]; + + v[0] = addConstant(Constant.UTF8, ((NameAndType) value).name()); + v[1] = addConstant(Constant.UTF8, ((NameAndType) value).type() + .descriptor()); + + c = new Constant(tag, v); + break; + } + case Constant.UTF8: { + final String s = (String) value; + c = new Constant(tag, s.intern()); + break; + } + default: { + c = new Constant(tag, value); + break; + } + } + + Integer index = (Integer) constantIndices.get(c); + + if (index == null) { + index = new Integer(constants.size()); + constantIndices.put(c, index); + constants.add(c); + resolved.add(value); + + if ((tag == Constant.LONG) || (tag == Constant.DOUBLE)) { + constants.add(null); + resolved.add(null); + } + } + + return index.intValue(); + } + + /** + * Get an array of the constants in the pool. + * + * @return An array of the constants in the pool. + */ + public Constant[] constants() { + final Object[] a = constants.toArray(); + final Constant[] array = new Constant[a.length]; + System.arraycopy(a, 0, array, 0, a.length); + return array; + } + + /** + * Returns an unmodifiable List of constants in this constant pool. + */ + public List getConstantsList() { + return Collections.unmodifiableList(this.constants); + } +} 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..f483890 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/EditorContext.java @@ -0,0 +1,153 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * 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 ClassInfo 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). + */ + public ClassInfo newClassInfo(int modifiers, int classIndex, + int superClassIndex, int[] interfaceIndexes, + java.util.List constants); + + /** + * Returns the ClassHierarchy of all classes and interfaces known + * to BLOAT. + */ + public ClassHierarchy getHierarchy(); + + /** + * 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 ClassEditor newClass(int modifiers, String className, + Type superType, Type[] interfaces); + + /** + * Returns a ClassEditor used to edit a class of a given name. + */ + public ClassEditor editClass(String className) + throws ClassNotFoundException, ClassFormatException; + + /** + * Returns a ClassEditor used to edit a class described by a + * given Type. + */ + public ClassEditor editClass(Type classType) throws ClassNotFoundException, + ClassFormatException; + + /** + * Returns a ClassEditor used to edit a class described by a + * given ClassInfo. + */ + public ClassEditor editClass(ClassInfo info); + + /** + * Returns a FieldEditor for editing a FieldInfo. + */ + public FieldEditor editField(FieldInfo info); + + /** + * Returns a FieldEditor for editing a field. + */ + public FieldEditor editField(MemberRef field) throws NoSuchFieldException; + + /** + * Returns a MethodEditor for editing a method. + */ + public MethodEditor editMethod(MethodInfo info); + + /** + * Returns a MethodEditor for editing a method. + */ + public MethodEditor editMethod(MemberRef method) + throws NoSuchMethodException; + + /** + * Signals that we are done editing a method. The object used to model it + * may be reclaimed. + */ + public void release(MethodInfo info); + + /** + * Signals that we are done editing a field. The object used to model it may + * be reclaimed. + */ + public void release(FieldInfo info); + + /** + * Signals that we are done editing a class. The object used to model it may + * be reclaimed. + */ + public void release(ClassInfo info); + + /** + * Commits the changes made to a class. + */ + public void commit(ClassInfo info); + + /** + * Commits the changes made to a method. + */ + public void commit(MethodInfo info); + + /** + * Commits the changes made to a field. + */ + public void commit(FieldInfo info); + + /** + * Commits all changes made to classes, methods, and fields. + */ + public void commit(); +} diff --git a/src/EDU/purdue/cs/bloat/editor/EditorVisitor.java b/src/EDU/purdue/cs/bloat/editor/EditorVisitor.java new file mode 100644 index 0000000..96ab694 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/EditorVisitor.java @@ -0,0 +1,35 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * EditorVisitor "visits" the "nodes" in a class. Imagine that a + * class is rooted by a ClassEditor that has FieldEditor + * and MethodEditor children. + */ + +public interface EditorVisitor { + public void visitClassEditor(ClassEditor editor); + + public void visitMethodEditor(MethodEditor editor); + + public void visitFieldEditor(FieldEditor editor); +} diff --git a/src/EDU/purdue/cs/bloat/editor/FieldEditor.java b/src/EDU/purdue/cs/bloat/editor/FieldEditor.java new file mode 100644 index 0000000..d26003f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/FieldEditor.java @@ -0,0 +1,562 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * FieldEditor provides a means to edit a field of a class. A + * FieldEditor is created from a ClassEditor and a + * reflect.FieldInfo. A FieldEditor knows its name, type + * (descriptor), and its constant value (if it has one). + * + * @see EDU.purdue.cs.bloat.reflect.FieldInfo + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class FieldEditor { + private ClassEditor editor; + + private FieldInfo fieldInfo; + + private String name; + + private Type type; + + private Object constantValue; + + private boolean isDirty; + + private boolean isDeleted = false; + + /** + * Creates a new FieldEditor for editing a field in a given + * class with the given modifiers, type and name + * + * @throws IllegalArgumentException + * If a field with the desired name already exists in the class + */ + public FieldEditor(final ClassEditor editor, final int modifiers, + final Type type, final String name) { + + this(editor, modifiers, type, name, null); + } + + public FieldEditor(final ClassEditor editor, final int modifiers, + final Class type, final String name, final Object constantValue) { + this(editor, modifiers, Type.getType(type), name, constantValue); + } + + public FieldEditor(final ClassEditor editor, final int modifiers, + final Class type, final String name) { + this(editor, modifiers, Type.getType(type), name, null); + } + + /** + * Creates a new FieldEditor for editing a field in a given + * class with the given modifiers, type, name, and constant value. + * + * @param modifiers + * Fields that have a constant value must be static + * and final + * + * @throws IllegalArgumentException + * If a field with the desired name already exists in the class + * or if constantValue is non-null and neither a + * String, Integer, + * Long, Float, nor + * Double. + */ + public FieldEditor(final ClassEditor editor, final int modifiers, + final Type type, final String name, final Object constantValue) { + + // Does the class already have a field with this name? + final FieldInfo[] fields = editor.fields(); + for (int i = 0; i < fields.length; i++) { + final FieldEditor fe = new FieldEditor(editor, fields[i]); + if (fe.name().equals(name)) { + final String s = "A field named " + name + + " already exists in " + editor.name(); + throw new IllegalArgumentException(s); + } + } + + this.editor = editor; + + final ConstantPool cp = editor.constants(); + this.name = name; + this.type = type; + + final int typeIndex = cp.getUTF8Index(this.type.descriptor()); + final int nameIndex = cp.getUTF8Index(name); + + final ClassInfo classInfo = editor.classInfo(); + + if (constantValue != null) { + // Only static final field may have constant values + if (((modifiers & Modifiers.STATIC) == 0) + || ((modifiers & Modifiers.FINAL) == 0)) { + final String s = "Field " + name + + " with a constant value must be static and final"; + throw new IllegalArgumentException(s); + } + + // Create an entry in the constant pool for the constant value + // of this field + int valueIndex; + if (constantValue instanceof String) { + if (!type.equals(Type.STRING)) { + final String s = "Can't have field type of " + + type.className() + " with a constant value of \"" + + constantValue + "\""; + throw new IllegalArgumentException(s); + } + valueIndex = cp.getStringIndex((String) constantValue); + + } else if (constantValue instanceof Integer) { + if (!type.equals(Type.INTEGER)) { + final String s = "Can't have field type of " + + type.className() + " with a constant value of \"" + + constantValue + "\""; + throw new IllegalArgumentException(s); + } + valueIndex = cp.getIntegerIndex((Integer) constantValue); + + } else if (constantValue instanceof Long) { + if (!type.equals(Type.LONG)) { + final String s = "Can't have field type of " + + type.className() + " with a constant value of \"" + + constantValue + "\""; + throw new IllegalArgumentException(s); + } + valueIndex = cp.getLongIndex((Long) constantValue); + + } else if (constantValue instanceof Float) { + if (!type.equals(Type.FLOAT)) { + final String s = "Can't have field type of " + + type.className() + " with a constant value of \"" + + constantValue + "\""; + throw new IllegalArgumentException(s); + } + valueIndex = cp.getFloatIndex((Float) constantValue); + + } else if (constantValue instanceof Double) { + if (!type.equals(Type.DOUBLE)) { + final String s = "Can't have field type of " + + type.className() + " with a constant value of \"" + + constantValue + "\""; + throw new IllegalArgumentException(s); + } + valueIndex = cp.getDoubleIndex((Double) constantValue); + + } else { + final String s = "Cannot have a constant value of type " + + constantValue.getClass().getName(); + throw new IllegalArgumentException(s); + } + + this.constantValue = constantValue; + + final int cvNameIndex = cp.getUTF8Index("ConstantValue"); + this.fieldInfo = classInfo.addNewField(modifiers, typeIndex, + nameIndex, cvNameIndex, valueIndex); + + } else { + this.fieldInfo = classInfo.addNewField(modifiers, typeIndex, + nameIndex); + } + + this.isDirty = true; + } + + /** + * Constructor. + * + * @param editor + * The class containing the field. + * @param fieldInfo + * The field to edit. + * + * @see ClassEditor + * @see FieldInfo + */ + public FieldEditor(final ClassEditor editor, final FieldInfo fieldInfo) { + final ConstantPool cp = editor.constants(); + + this.fieldInfo = fieldInfo; + this.editor = editor; + + int index; + + index = fieldInfo.nameIndex(); + name = (String) cp.constantAt(index); + + index = fieldInfo.typeIndex(); + final String typeName = (String) cp.constantAt(index); + type = Type.getType(typeName); + + index = fieldInfo.constantValue(); + constantValue = cp.constantAt(index); + this.isDirty = false; + } + + /** + * Returns the ClassEditor used to edit the class in which this + * field resides. + */ + public ClassEditor declaringClass() { + return editor; + } + + /** + * Returns true if this field has been modified. + */ + public boolean isDirty() { + return (this.isDirty); + } + + /** + * Sets the dirty flag of this method. The dirty flag is true if + * the method has been modified. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void setDirty(final boolean isDirty) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + this.isDirty = isDirty; + if (isDirty == true) { + this.editor.setDirty(true); + } + } + + /** + * Marks this field for deletion. Once a field has been marked for deletion + * all attempts to change it will throw an + * IllegalStateException. + */ + public void delete() { + this.setDirty(true); + this.isDeleted = true; + } + + /** + * Returns the raw FieldInfo of the field being edited. + */ + public FieldInfo fieldInfo() { + return fieldInfo; + } + + public Object constantValue() { + return constantValue; + } + + public boolean isPublic() { + return (fieldInfo.modifiers() & Modifiers.PUBLIC) != 0; + } + + public boolean isPrivate() { + return (fieldInfo.modifiers() & Modifiers.PRIVATE) != 0; + } + + public boolean isProtected() { + return (fieldInfo.modifiers() & Modifiers.PROTECTED) != 0; + } + + /** + * Returns true, if the field has package level visibility. + */ + public boolean isPackage() { + return (!isPublic() && !isPrivate() && !isProtected()); + } + + public boolean isStatic() { + return (fieldInfo.modifiers() & Modifiers.STATIC) != 0; + } + + public boolean isFinal() { + return (fieldInfo.modifiers() & Modifiers.FINAL) != 0; + } + + public boolean isVolatile() { + return (fieldInfo.modifiers() & Modifiers.VOLATILE) != 0; + } + + public boolean isTransient() { + return (fieldInfo.modifiers() & Modifiers.TRANSIENT) != 0; + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setPublic(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PUBLIC; + } else { + modifiers &= ~Modifiers.PUBLIC; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setPrivate(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PRIVATE; + } else { + modifiers &= ~Modifiers.PRIVATE; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setProtected(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.PROTECTED; + } else { + modifiers &= ~Modifiers.PROTECTED; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setStatic(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.STATIC; + } else { + modifiers &= ~Modifiers.STATIC; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setFinal(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.FINAL; + } else { + modifiers &= ~Modifiers.FINAL; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setTransient(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.TRANSIENT; + } else { + modifiers &= ~Modifiers.TRANSIENT; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setVolatile(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int modifiers = fieldInfo.modifiers(); + + if (flag) { + modifiers |= Modifiers.VOLATILE; + } else { + modifiers &= ~Modifiers.VOLATILE; + } + + fieldInfo.setModifiers(modifiers); + this.setDirty(true); + } + + /** + * Returns the name of the field. + */ + public String name() { + return name; + } + + /** + * Returns the type of the field. + */ + public Type type() { + return type; + } + + /** + * Returns a NameAndType of the field. + */ + public NameAndType nameAndType() { + return (new NameAndType(this.name(), this.type())); + } + + /** + * Returns a MemberRef for the field + */ + public MemberRef memberRef() { + return (new MemberRef(this.declaringClass().type(), this.nameAndType())); + } + + /** + * Commit changes to the field back to the ClassEditor. Note that the field + * is committed regardless of whether or not it is dirty. + */ + public void commit() { + if (this.isDeleted) { + // Even if the field is newly-added, we can still delete it + // without problems because its FieldInfo was already noted with + // the ClassInfo. + + final ConstantPool cp = editor.constants(); + final int nameIndex = cp.getUTF8Index(name); + this.editor.classInfo().deleteField(nameIndex); + + } else { + final ConstantPool cp = editor.constants(); + + fieldInfo.setNameIndex(cp.addConstant(Constant.UTF8, name)); + fieldInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type + .descriptor())); + + if (constantValue != null) { + if (constantValue instanceof Long) { + fieldInfo.setConstantValue(cp.addConstant(Constant.LONG, + constantValue)); + } else if (constantValue instanceof Float) { + fieldInfo.setConstantValue(cp.addConstant(Constant.FLOAT, + constantValue)); + } else if (constantValue instanceof Double) { + fieldInfo.setConstantValue(cp.addConstant(Constant.DOUBLE, + constantValue)); + } else if (constantValue instanceof Integer) { + fieldInfo.setConstantValue(cp.addConstant(Constant.INTEGER, + constantValue)); + } else if (constantValue instanceof String) { + fieldInfo.setConstantValue(cp.addConstant(Constant.STRING, + constantValue)); + } + } + } + + // This field is no longer dirty + this.isDirty = false; + } + + /** + * Print the field. + * + * @param out + * Stream to which to print. + */ + public void print(final PrintStream out) { + out.println("field " + name + " " + type); + } + + /** + * Returns a String that contains the declaring class name and the name of + * the field + */ + public String fullName() { + return declaringClass().name() + "." + this.name(); + } + + public String toString() { + return ("[FieldEditor for " + this.name + this.type + "]"); + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/IncOperand.java b/src/EDU/purdue/cs/bloat/editor/IncOperand.java new file mode 100644 index 0000000..054986a --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/IncOperand.java @@ -0,0 +1,75 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * IncOperand encapsulates the operands to the iinc instruction. It is necessary + * because the iinc has two operands: a local variable and an integer + * by which to increment the local variable. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class IncOperand { + private LocalVariable var; + + private int incr; + + /** + * Constructor. + * + * @param var + * The local variable to increment. + * @param incr + * The amount to increment by. + */ + public IncOperand(final LocalVariable var, final int incr) { + this.var = var; + this.incr = incr; + } + + /** + * Get the local variable to increment. + * + * @return The local variable to increment. + */ + public LocalVariable var() { + return var; + } + + /** + * Get the amount to increment by. + * + * @return The amount to increment by. + */ + public int incr() { + return incr; + } + + /** + * Convert the operand to a string. + * + * @return A string representation of the operand. + */ + public String toString() { + return "" + var + " by " + incr; + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/Instruction.java b/src/EDU/purdue/cs/bloat/editor/Instruction.java new file mode 100644 index 0000000..db7d6de --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Instruction.java @@ -0,0 +1,1258 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import EDU.purdue.cs.bloat.util.*; + +/** + * Instruction represents a single instruction in the JVM. All + * Instructions known their opcode. Some instructions have an + * operand. Operands are integers, floats, or one of the special classes that + * represent multiple operands such as IncOperand and Switch. + * + * @see IncOperand + * @see Switch + * @see MultiArrayOperand + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Instruction implements Opcode { + private Object operand; + + private int opcode; // Mapped opcode + + private int origOpcode; // Original (non-mapped) opcode + + private boolean useSlow = false; + + // Do we use a slow version when generating code? + + /** + * Constructor. + * + * @param opcode + * The opcode class of the instruction. + */ + public Instruction(final int opcode) { + this(opcode, null); + } + + /** + * Constructor. + * + * @param opcode + * The opcode class of the instruction. + * @param operand + * The operand of the instruction, or null. + */ + public Instruction(final int opcode, final Object operand) { + this.opcode = opcode; + this.origOpcode = opcode; + this.operand = operand; + Assert.isTrue(opcode == Opcode.opcXMap[opcode], "Illegal instruction: " + + this); + } + + /** + * Constructor. + * + * @param code + * The raw byte code array of the method. + * @param index + * This index into the byte array of the instruction. + * @param targets + * Indices of the branch targets of the instruction. + * @param lookups + * Values of the switch lookups of the instruction. + * @param locals + * The local variables defined at this index. + * @param constants + * The constant pool for the class. + */ + public Instruction(final byte[] code, final int index, final int[] targets, + final int[] lookups, final LocalVariable[] locals, + final ConstantPool constants) { + int i, j; + int incr; + int dim; + int atype; + Label[] t; + int[] v; + + int opc = Instruction.toUByte(code[index]); + + switch (opc) { + case opc_aconst_null: + operand = null; + break; + case opc_iconst_m1: + operand = new Integer(-1); + break; + case opc_iconst_0: + operand = new Integer(0); + break; + case opc_iconst_1: + operand = new Integer(1); + break; + case opc_iconst_2: + operand = new Integer(2); + break; + case opc_iconst_3: + operand = new Integer(3); + break; + case opc_iconst_4: + operand = new Integer(4); + break; + case opc_iconst_5: + operand = new Integer(5); + break; + case opc_lconst_0: + operand = new Long(0); + break; + case opc_lconst_1: + operand = new Long(1); + break; + case opc_fconst_0: + operand = new Float(0.0F); + break; + case opc_fconst_1: + operand = new Float(1.0F); + break; + case opc_fconst_2: + operand = new Float(2.0F); + break; + case opc_dconst_0: + operand = new Double(0.0); + break; + case opc_dconst_1: + operand = new Double(1.0); + break; + case opc_bipush: + operand = new Integer(code[index + 1]); + break; + case opc_sipush: + operand = new Integer(Instruction.toShort(code[index + 1], + code[index + 2])); + break; + case opc_ldc: + i = Instruction.toUByte(code[index + 1]); + operand = constants.constantAt(i); + break; + case opc_ldc_w: + case opc_ldc2_w: + i = Instruction.toUShort(code[index + 1], code[index + 2]); + operand = constants.constantAt(i); + break; + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: + i = Instruction.toUByte(code[index + 1]); + operand = (i < locals.length) && (locals[i] != null) ? locals[i] + : new LocalVariable(i); + break; + case opc_iload_0: + case opc_lload_0: + case opc_fload_0: + case opc_dload_0: + case opc_aload_0: + operand = (0 < locals.length) && (locals[0] != null) ? locals[0] + : new LocalVariable(0); + break; + case opc_iload_1: + case opc_lload_1: + case opc_fload_1: + case opc_dload_1: + case opc_aload_1: + operand = (1 < locals.length) && (locals[1] != null) ? locals[1] + : new LocalVariable(1); + break; + case opc_iload_2: + case opc_lload_2: + case opc_fload_2: + case opc_dload_2: + case opc_aload_2: + operand = (2 < locals.length) && (locals[2] != null) ? locals[2] + : new LocalVariable(2); + break; + case opc_iload_3: + case opc_lload_3: + case opc_fload_3: + case opc_dload_3: + case opc_aload_3: + operand = (3 < locals.length) && (locals[3] != null) ? locals[3] + : new LocalVariable(3); + break; + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + i = Instruction.toUByte(code[index + 1]); + operand = (i < locals.length) && (locals[i] != null) ? locals[i] + : new LocalVariable(i); + break; + case opc_istore_0: + case opc_lstore_0: + case opc_fstore_0: + case opc_dstore_0: + case opc_astore_0: + operand = (0 < locals.length) && (locals[0] != null) ? locals[0] + : new LocalVariable(0); + break; + case opc_istore_1: + case opc_lstore_1: + case opc_fstore_1: + case opc_dstore_1: + case opc_astore_1: + operand = (1 < locals.length) && (locals[1] != null) ? locals[1] + : new LocalVariable(1); + break; + case opc_istore_2: + case opc_lstore_2: + case opc_fstore_2: + case opc_dstore_2: + case opc_astore_2: + operand = (2 < locals.length) && (locals[2] != null) ? locals[2] + : new LocalVariable(2); + break; + case opc_istore_3: + case opc_lstore_3: + case opc_fstore_3: + case opc_dstore_3: + case opc_astore_3: + operand = (3 < locals.length) && (locals[3] != null) ? locals[3] + : new LocalVariable(3); + break; + case opc_iinc: + i = Instruction.toUByte(code[index + 1]); + incr = code[index + 2]; + operand = new IncOperand( + (i < locals.length) && (locals[i] != null) ? locals[i] + : new LocalVariable(i), incr); + break; + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmplt: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_acmpeq: + case opc_if_acmpne: + Assert.isTrue(targets.length == 1, "Illegal instruction: " + + Opcode.opcNames[opc]); + operand = new Label(targets[0]); + break; + case opc_goto: + case opc_jsr: + case opc_ifnull: + case opc_ifnonnull: + case opc_goto_w: + case opc_jsr_w: + Assert.isTrue(targets.length == 1, "Illegal instruction: " + + Opcode.opcNames[opc]); + operand = new Label(targets[0]); + break; + case opc_ret: + i = Instruction.toUByte(code[index + 1]); + operand = (i < locals.length) && (locals[i] != null) ? locals[i] + : new LocalVariable(i); + break; + case opc_tableswitch: + // The first target is the default. + t = new Label[targets.length - 1]; + v = new int[targets.length - 1]; + for (i = 1, j = lookups[0]; i < targets.length; i++, j++) { + t[i - 1] = new Label(targets[i]); + v[i - 1] = j; + } + operand = new Switch(new Label(targets[0]), t, v); + break; + case opc_lookupswitch: + // The first target is the default. + t = new Label[targets.length - 1]; + v = new int[targets.length - 1]; + for (i = 1; i < targets.length; i++) { + t[i - 1] = new Label(targets[i]); + v[i - 1] = lookups[i - 1]; + } + operand = new Switch(new Label(targets[0]), t, v); + break; + case opc_getstatic: + case opc_putstatic: + case opc_putstatic_nowb: + case opc_getfield: + case opc_putfield: + case opc_putfield_nowb: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + case opc_new: + case opc_anewarray: + case opc_checkcast: + case opc_instanceof: + i = Instruction.toUShort(code[index + 1], code[index + 2]); + operand = constants.constantAt(i); + break; + case opc_newarray: + atype = code[index + 1]; + operand = Type.getType(atype); + break; + case opc_wide: + opc = Instruction.toUByte(code[index + 1]); + switch (opc) { + case opc_iload: + case opc_fload: + case opc_aload: + case opc_lload: + case opc_dload: + case opc_istore: + case opc_fstore: + case opc_astore: + case opc_lstore: + case opc_dstore: + case opc_ret: + i = Instruction.toUShort(code[index + 2], code[index + 3]); + operand = (i < locals.length) && (locals[i] != null) ? locals[i] + : new LocalVariable(i); + break; + case opc_iinc: + i = Instruction.toUShort(code[index + 2], code[index + 3]); + incr = Instruction.toShort(code[index + 4], code[index + 5]); + operand = new IncOperand((i < locals.length) + && (locals[i] != null) ? locals[i] : new LocalVariable( + i), incr); + break; + } + break; + case opc_multianewarray: + i = Instruction.toUShort(code[index + 1], code[index + 2]); + dim = Instruction.toUByte(code[index + 3]); + operand = new MultiArrayOperand((Type) constants.constantAt(i), dim); + break; + case opc_rc: + i = Instruction.toUByte(code[index + 1]); + operand = new Integer(i); + break; + case opc_aupdate: + i = Instruction.toUByte(code[index + 1]); + operand = new Integer(i); + break; + case opc_supdate: + i = Instruction.toUByte(code[index + 1]); + operand = new Integer(i); + break; + default: + break; + } + + origOpcode = opc; + opcode = Opcode.opcXMap[opc]; + } + + /** + * Returns the original (non-mapped) opcode used to create this Instruction. + */ + public int origOpcode() { + return (this.origOpcode); + } + + /** + * Sets a flag that determines whether or not the "slow" version of the + * instruction should be generated. For example, if useSlow is true, "iload + * 2" is generated instead of "iload_2". + */ + public void setUseSlow(final boolean useSlow) { + this.useSlow = useSlow; + } + + /** + * Returns whether or not the "slow" version of this instruction is + * generated. + */ + public boolean useSlow() { + return (this.useSlow); + } + + /** + * Check if the instruction is a load. + * + * @return true if the instruction is a load, false if not. + */ + public boolean isLoad() { + switch (opcode) { + case opc_iload: + case opc_lload: + case opc_fload: + case opc_dload: + case opc_aload: + case opc_iload_0: + case opc_lload_0: + case opc_fload_0: + case opc_dload_0: + case opc_aload_0: + case opc_iload_1: + case opc_lload_1: + case opc_fload_1: + case opc_dload_1: + case opc_aload_1: + case opc_iload_2: + case opc_lload_2: + case opc_fload_2: + case opc_dload_2: + case opc_aload_2: + case opc_iload_3: + case opc_lload_3: + case opc_fload_3: + case opc_dload_3: + case opc_aload_3: + return true; + default: + return false; + } + } + + /** + * Check if the instruction is a store. + * + * @return true if the instruction is a store, false if not. + */ + public boolean isStore() { + switch (opcode) { + case opc_istore: + case opc_lstore: + case opc_fstore: + case opc_dstore: + case opc_astore: + case opc_istore_0: + case opc_lstore_0: + case opc_fstore_0: + case opc_dstore_0: + case opc_astore_0: + case opc_istore_1: + case opc_lstore_1: + case opc_fstore_1: + case opc_dstore_1: + case opc_astore_1: + case opc_istore_2: + case opc_lstore_2: + case opc_fstore_2: + case opc_dstore_2: + case opc_astore_2: + case opc_istore_3: + case opc_lstore_3: + case opc_fstore_3: + case opc_dstore_3: + case opc_astore_3: + return true; + default: + return false; + } + } + + /** + * Check if the instruction is an increment. + * + * @return true if the instruction is an increment, false if not. + */ + public boolean isInc() { + return opcode == Opcode.opc_iinc; + } + + /** + * Check if the instruction is an exception throw instruction. + * + * @return true if the instruction is a throw, false if not. + */ + public boolean isThrow() { + return opcode == Opcode.opc_athrow; + } + + /** + * Returns true if this instruction invokes a method. + */ + public boolean isInvoke() { + switch (opcode) { + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: + case opc_invokeinterface: + return (true); + default: + return (false); + } + } + + /** + * Check if the instruction is a subroutine return instruction. + * + * @return true if the instruction is a ret, false if not. + */ + public boolean isRet() { + return opcode == Opcode.opc_ret; + } + + /** + * Returns true if the instruction returns from a method. + */ + public boolean isReturn() { + switch (opcode) { + case opc_areturn: + case opc_ireturn: + case opc_lreturn: + case opc_freturn: + case opc_dreturn: + case opc_return: + return true; + default: + return false; + } + } + + /** + * Check if the instruction is a switch. + * + * @return true if the instruction is a switch, false if not. + */ + public boolean isSwitch() { + return opcodeClass() == Opcode.opcx_switch; + } + + /** + * Check if the instruction is a jump. + * + * @return true if the instruction is a jump, false if not. + */ + public boolean isJump() { + return isConditionalJump() || isGoto(); + } + + /** + * Check if the instruction is a jsr. + * + * @return true if the instruction is a jsr, false if not. + */ + public boolean isJsr() { + return opcodeClass() == Opcode.opcx_jsr; + } + + /** + * Check if the instruction is a goto. + * + * @return true if the instruction is a goto, false if not. + */ + public boolean isGoto() { + return opcodeClass() == Opcode.opcx_goto; + } + + /** + * Check if the instruction is a conditional jump. + * + * @return true if the instruction is a conditional jump, false if not. + */ + public boolean isConditionalJump() { + switch (opcode) { + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmplt: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_ifnull: + case opc_ifnonnull: + return true; + default: + return false; + } + } + + /** + * Get the opcode class of the instruction. + * + * @return The opcode class of the instruction. + */ + public int opcodeClass() { + return opcode; + } + + /** + * Set the opcode class of the instruction. + * + * @param opcode + * The opcode class of the instruction. + */ + public void setOpcodeClass(final int opcode) { + this.opcode = opcode; + } + + /** + * Set the operand of the instruction. + * + * @param operand + * The operand of the instruction. + */ + public void setOperand(final Object operand) { + this.operand = operand; + } + + /** + * Get the operand of the instruction. + * + * @return The operand of the instruction. + */ + public Object operand() { + return operand; + } + + /** + * Convert the instruction to a string. + * + * @return A string representation of the instruction. + */ + public String toString() { + if ((operand == null) && (opcodeClass() != Opcode.opcx_ldc)) { + return Opcode.opcNames[opcode]; + } else if (operand instanceof Float) { + return Opcode.opcNames[opcode] + " " + operand + "F"; + } else if (operand instanceof Long) { + return Opcode.opcNames[opcode] + " " + operand + "L"; + } else if (operand instanceof String) { + final StringBuffer sb = new StringBuffer(); + + final String s = (String) operand; + + for (int i = 0; i < s.length(); i++) { + final 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; + } + } + + return Opcode.opcNames[opcode] + " \"" + sb.toString() + "\""; + + } else { + + return Opcode.opcNames[opcode] + " " + operand; + } + } + + /** + * Utility function to join 2 bytes into an unsigned short. + * + * @param b1 + * The upper byte. + * @param b2 + * The lower byte. + * @return The unsigned short. + */ + protected static int toUShort(final byte b1, final byte b2) { + int x = (short) (Instruction.toUByte(b1) << 8) + | Instruction.toUByte(b2); + if (x < 0) { + x += 0x10000; + } + return x; + } + + /** + * Utility function to join 2 bytes into an signed short. + * + * @param b1 + * The upper byte. + * @param b2 + * The lower byte. + * @return The signed short. + */ + protected static short toShort(final byte b1, final byte b2) { + return (short) ((Instruction.toUByte(b1) << 8) | Instruction + .toUByte(b2)); + } + + /** + * Utility function to join 4 bytes into an signed int. + * + * @param b1 + * The upper byte. + * @param b2 + * The next-to-upper byte. + * @param b3 + * The next-to-lower byte. + * @param b4 + * The lower byte. + * @return The signed int. + */ + protected static int toInt(final byte b1, final byte b2, final byte b3, + final byte b4) { + return (Instruction.toUByte(b1) << 24) + | (Instruction.toUByte(b2) << 16) + | (Instruction.toUByte(b3) << 8) | Instruction.toUByte(b4); + } + + /** + * Utility function to convert a byte into an unsigned byte. + * + * @param b + * The byte. + * @return The unsigned byte. + */ + protected static int toUByte(final byte b) { + return b < 0 ? b + 0x100 : b; + } + + /** + * Returns the category of this instruction. An instruction's category is + * basically the width of the value the instruction places on the stack. + * Types long and double are Category 2. All other + * types are Category 1. + */ + public int category() { + switch (this.opcode) { + case opcx_lload: + case opcx_dload: + case opcx_lstore: + case opcx_dstore: + case opcx_laload: + case opcx_daload: + case opcx_lastore: + case opcx_dastore: + case opcx_ladd: + case opcx_dadd: + case opcx_lsub: + case opcx_dsub: + case opcx_lmul: + case opcx_dmul: + case opcx_ldiv: + case opcx_ddiv: + case opcx_lrem: + case opcx_drem: + case opcx_lneg: + case opcx_dneg: + case opcx_i2l: + case opcx_i2d: + case opcx_l2d: + case opcx_f2l: + case opcx_f2d: + case opcx_d2l: + case opcx_land: + case opcx_lor: + case opcx_lxor: + case opcx_lshl: + case opcx_lshr: + case opcx_lushr: + return (2); + + case opcx_ldc: + // If we're loading a Long or Double, the category is 2. + if ((this.operand instanceof Long) + || (this.operand instanceof Double)) { + return (2); + + } else { + return (1); + } + + case opcx_invokevirtual: + case opcx_invokespecial: + case opcx_invokeinterface: + case opcx_invokestatic: + // If the return type is wide, then the category is 2. + final MemberRef callee = (MemberRef) this.operand; + if (callee.nameAndType().type().returnType().isWide()) { + return (2); + + } else { + return (1); + } + + default: + return (1); + } + } + + /** + * Big switch statement to call the appropriate method of an instruction + * visitor. + * + * @param visitor + * The instruction visitor. + */ + public void visit(final InstructionVisitor visitor) { + switch (opcodeClass()) { + case opcx_nop: + visitor.visit_nop(this); + break; + case opcx_ldc: + visitor.visit_ldc(this); + break; + case opcx_iload: + visitor.visit_iload(this); + break; + case opcx_lload: + visitor.visit_lload(this); + break; + case opcx_fload: + visitor.visit_fload(this); + break; + case opcx_dload: + visitor.visit_dload(this); + break; + case opcx_aload: + visitor.visit_aload(this); + break; + case opcx_iaload: + visitor.visit_iaload(this); + break; + case opcx_laload: + visitor.visit_laload(this); + break; + case opcx_faload: + visitor.visit_faload(this); + break; + case opcx_daload: + visitor.visit_daload(this); + break; + case opcx_aaload: + visitor.visit_aaload(this); + break; + case opcx_baload: + visitor.visit_baload(this); + break; + case opcx_caload: + visitor.visit_caload(this); + break; + case opcx_saload: + visitor.visit_saload(this); + break; + case opcx_istore: + visitor.visit_istore(this); + break; + case opcx_lstore: + visitor.visit_lstore(this); + break; + case opcx_fstore: + visitor.visit_fstore(this); + break; + case opcx_dstore: + visitor.visit_dstore(this); + break; + case opcx_astore: + visitor.visit_astore(this); + break; + case opcx_iastore: + visitor.visit_iastore(this); + break; + case opcx_lastore: + visitor.visit_lastore(this); + break; + case opcx_fastore: + visitor.visit_fastore(this); + break; + case opcx_dastore: + visitor.visit_dastore(this); + break; + case opcx_aastore: + visitor.visit_aastore(this); + break; + case opcx_bastore: + visitor.visit_bastore(this); + break; + case opcx_castore: + visitor.visit_castore(this); + break; + case opcx_sastore: + visitor.visit_sastore(this); + break; + case opcx_pop: + visitor.visit_pop(this); + break; + case opcx_pop2: + visitor.visit_pop2(this); + break; + case opcx_dup: + visitor.visit_dup(this); + break; + case opcx_dup_x1: + visitor.visit_dup_x1(this); + break; + case opcx_dup_x2: + visitor.visit_dup_x2(this); + break; + case opcx_dup2: + visitor.visit_dup2(this); + break; + case opcx_dup2_x1: + visitor.visit_dup2_x1(this); + break; + case opcx_dup2_x2: + visitor.visit_dup2_x2(this); + break; + case opcx_swap: + visitor.visit_swap(this); + break; + case opcx_iadd: + visitor.visit_iadd(this); + break; + case opcx_ladd: + visitor.visit_ladd(this); + break; + case opcx_fadd: + visitor.visit_fadd(this); + break; + case opcx_dadd: + visitor.visit_dadd(this); + break; + case opcx_isub: + visitor.visit_isub(this); + break; + case opcx_lsub: + visitor.visit_lsub(this); + break; + case opcx_fsub: + visitor.visit_fsub(this); + break; + case opcx_dsub: + visitor.visit_dsub(this); + break; + case opcx_imul: + visitor.visit_imul(this); + break; + case opcx_lmul: + visitor.visit_lmul(this); + break; + case opcx_fmul: + visitor.visit_fmul(this); + break; + case opcx_dmul: + visitor.visit_dmul(this); + break; + case opcx_idiv: + visitor.visit_idiv(this); + break; + case opcx_ldiv: + visitor.visit_ldiv(this); + break; + case opcx_fdiv: + visitor.visit_fdiv(this); + break; + case opcx_ddiv: + visitor.visit_ddiv(this); + break; + case opcx_irem: + visitor.visit_irem(this); + break; + case opcx_lrem: + visitor.visit_lrem(this); + break; + case opcx_frem: + visitor.visit_frem(this); + break; + case opcx_drem: + visitor.visit_drem(this); + break; + case opcx_ineg: + visitor.visit_ineg(this); + break; + case opcx_lneg: + visitor.visit_lneg(this); + break; + case opcx_fneg: + visitor.visit_fneg(this); + break; + case opcx_dneg: + visitor.visit_dneg(this); + break; + case opcx_ishl: + visitor.visit_ishl(this); + break; + case opcx_lshl: + visitor.visit_lshl(this); + break; + case opcx_ishr: + visitor.visit_ishr(this); + break; + case opcx_lshr: + visitor.visit_lshr(this); + break; + case opcx_iushr: + visitor.visit_iushr(this); + break; + case opcx_lushr: + visitor.visit_lushr(this); + break; + case opcx_iand: + visitor.visit_iand(this); + break; + case opcx_land: + visitor.visit_land(this); + break; + case opcx_ior: + visitor.visit_ior(this); + break; + case opcx_lor: + visitor.visit_lor(this); + break; + case opcx_ixor: + visitor.visit_ixor(this); + break; + case opcx_lxor: + visitor.visit_lxor(this); + break; + case opcx_iinc: + visitor.visit_iinc(this); + break; + case opcx_i2l: + visitor.visit_i2l(this); + break; + case opcx_i2f: + visitor.visit_i2f(this); + break; + case opcx_i2d: + visitor.visit_i2d(this); + break; + case opcx_l2i: + visitor.visit_l2i(this); + break; + case opcx_l2f: + visitor.visit_l2f(this); + break; + case opcx_l2d: + visitor.visit_l2d(this); + break; + case opcx_f2i: + visitor.visit_f2i(this); + break; + case opcx_f2l: + visitor.visit_f2l(this); + break; + case opcx_f2d: + visitor.visit_f2d(this); + break; + case opcx_d2i: + visitor.visit_d2i(this); + break; + case opcx_d2l: + visitor.visit_d2l(this); + break; + case opcx_d2f: + visitor.visit_d2f(this); + break; + case opcx_i2b: + visitor.visit_i2b(this); + break; + case opcx_i2c: + visitor.visit_i2c(this); + break; + case opcx_i2s: + visitor.visit_i2s(this); + break; + case opcx_lcmp: + visitor.visit_lcmp(this); + break; + case opcx_fcmpl: + visitor.visit_fcmpl(this); + break; + case opcx_fcmpg: + visitor.visit_fcmpg(this); + break; + case opcx_dcmpl: + visitor.visit_dcmpl(this); + break; + case opcx_dcmpg: + visitor.visit_dcmpg(this); + break; + case opcx_ifeq: + visitor.visit_ifeq(this); + break; + case opcx_ifne: + visitor.visit_ifne(this); + break; + case opcx_iflt: + visitor.visit_iflt(this); + break; + case opcx_ifge: + visitor.visit_ifge(this); + break; + case opcx_ifgt: + visitor.visit_ifgt(this); + break; + case opcx_ifle: + visitor.visit_ifle(this); + break; + case opcx_if_icmpeq: + visitor.visit_if_icmpeq(this); + break; + case opcx_if_icmpne: + visitor.visit_if_icmpne(this); + break; + case opcx_if_icmplt: + visitor.visit_if_icmplt(this); + break; + case opcx_if_icmpge: + visitor.visit_if_icmpge(this); + break; + case opcx_if_icmpgt: + visitor.visit_if_icmpgt(this); + break; + case opcx_if_icmple: + visitor.visit_if_icmple(this); + break; + case opcx_if_acmpeq: + visitor.visit_if_acmpeq(this); + break; + case opcx_if_acmpne: + visitor.visit_if_acmpne(this); + break; + case opcx_goto: + visitor.visit_goto(this); + break; + case opcx_jsr: + visitor.visit_jsr(this); + break; + case opcx_ret: + visitor.visit_ret(this); + break; + case opcx_switch: + visitor.visit_switch(this); + break; + case opcx_ireturn: + visitor.visit_ireturn(this); + break; + case opcx_lreturn: + visitor.visit_lreturn(this); + break; + case opcx_freturn: + visitor.visit_freturn(this); + break; + case opcx_dreturn: + visitor.visit_dreturn(this); + break; + case opcx_areturn: + visitor.visit_areturn(this); + break; + case opcx_return: + visitor.visit_return(this); + break; + case opcx_getstatic: + visitor.visit_getstatic(this); + break; + case opcx_putstatic: + visitor.visit_putstatic(this); + break; + case opcx_putstatic_nowb: + visitor.visit_putstatic_nowb(this); + break; + case opcx_getfield: + visitor.visit_getfield(this); + break; + case opcx_putfield: + visitor.visit_putfield(this); + break; + case opcx_putfield_nowb: + visitor.visit_putfield_nowb(this); + break; + case opcx_invokevirtual: + visitor.visit_invokevirtual(this); + break; + case opcx_invokespecial: + visitor.visit_invokespecial(this); + break; + case opcx_invokestatic: + visitor.visit_invokestatic(this); + break; + case opcx_invokeinterface: + visitor.visit_invokeinterface(this); + break; + case opcx_new: + visitor.visit_new(this); + break; + case opcx_newarray: + visitor.visit_newarray(this); + break; + case opcx_arraylength: + visitor.visit_arraylength(this); + break; + case opcx_athrow: + visitor.visit_athrow(this); + break; + case opcx_checkcast: + visitor.visit_checkcast(this); + break; + case opcx_instanceof: + visitor.visit_instanceof(this); + break; + case opcx_monitorenter: + visitor.visit_monitorenter(this); + break; + case opcx_monitorexit: + visitor.visit_monitorexit(this); + break; + case opcx_multianewarray: + visitor.visit_multianewarray(this); + break; + case opcx_ifnull: + visitor.visit_ifnull(this); + break; + case opcx_ifnonnull: + visitor.visit_ifnonnull(this); + break; + case opcx_rc: + visitor.visit_rc(this); + break; + case opcx_aupdate: + visitor.visit_aupdate(this); + break; + case opcx_supdate: + visitor.visit_supdate(this); + break; + case opcx_aswizzle: + visitor.visit_aswizzle(this); + break; + case opcx_aswrange: + visitor.visit_aswrange(this); + break; + } + } +} 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..740df82 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/InstructionAdapter.java @@ -0,0 +1,459 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * This adapter provides a default implementation for every method in + * InstructionVisitor. + */ +public class InstructionAdapter implements InstructionVisitor { + public void visit_nop(final Instruction inst) { + } + + public void visit_ldc(final Instruction inst) { + } + + public void visit_iload(final Instruction inst) { + } + + public void visit_lload(final Instruction inst) { + } + + public void visit_fload(final Instruction inst) { + } + + public void visit_dload(final Instruction inst) { + } + + public void visit_aload(final Instruction inst) { + } + + public void visit_iaload(final Instruction inst) { + } + + public void visit_laload(final Instruction inst) { + } + + public void visit_faload(final Instruction inst) { + } + + public void visit_daload(final Instruction inst) { + } + + public void visit_aaload(final Instruction inst) { + } + + public void visit_baload(final Instruction inst) { + } + + public void visit_caload(final Instruction inst) { + } + + public void visit_saload(final Instruction inst) { + } + + public void visit_istore(final Instruction inst) { + } + + public void visit_lstore(final Instruction inst) { + } + + public void visit_fstore(final Instruction inst) { + } + + public void visit_dstore(final Instruction inst) { + } + + public void visit_astore(final Instruction inst) { + } + + public void visit_iastore(final Instruction inst) { + } + + public void visit_lastore(final Instruction inst) { + } + + public void visit_fastore(final Instruction inst) { + } + + public void visit_dastore(final Instruction inst) { + } + + public void visit_aastore(final Instruction inst) { + } + + public void visit_bastore(final Instruction inst) { + } + + public void visit_castore(final Instruction inst) { + } + + public void visit_sastore(final Instruction inst) { + } + + public void visit_pop(final Instruction inst) { + } + + public void visit_pop2(final Instruction inst) { + } + + public void visit_dup(final Instruction inst) { + } + + public void visit_dup_x1(final Instruction inst) { + } + + public void visit_dup_x2(final Instruction inst) { + } + + public void visit_dup2(final Instruction inst) { + } + + public void visit_dup2_x1(final Instruction inst) { + } + + public void visit_dup2_x2(final Instruction inst) { + } + + public void visit_swap(final Instruction inst) { + } + + public void visit_iadd(final Instruction inst) { + } + + public void visit_ladd(final Instruction inst) { + } + + public void visit_fadd(final Instruction inst) { + } + + public void visit_dadd(final Instruction inst) { + } + + public void visit_isub(final Instruction inst) { + } + + public void visit_lsub(final Instruction inst) { + } + + public void visit_fsub(final Instruction inst) { + } + + public void visit_dsub(final Instruction inst) { + } + + public void visit_imul(final Instruction inst) { + } + + public void visit_lmul(final Instruction inst) { + } + + public void visit_fmul(final Instruction inst) { + } + + public void visit_dmul(final Instruction inst) { + } + + public void visit_idiv(final Instruction inst) { + } + + public void visit_ldiv(final Instruction inst) { + } + + public void visit_fdiv(final Instruction inst) { + } + + public void visit_ddiv(final Instruction inst) { + } + + public void visit_irem(final Instruction inst) { + } + + public void visit_lrem(final Instruction inst) { + } + + public void visit_frem(final Instruction inst) { + } + + public void visit_drem(final Instruction inst) { + } + + public void visit_ineg(final Instruction inst) { + } + + public void visit_lneg(final Instruction inst) { + } + + public void visit_fneg(final Instruction inst) { + } + + public void visit_dneg(final Instruction inst) { + } + + public void visit_ishl(final Instruction inst) { + } + + public void visit_lshl(final Instruction inst) { + } + + public void visit_ishr(final Instruction inst) { + } + + public void visit_lshr(final Instruction inst) { + } + + public void visit_iushr(final Instruction inst) { + } + + public void visit_lushr(final Instruction inst) { + } + + public void visit_iand(final Instruction inst) { + } + + public void visit_land(final Instruction inst) { + } + + public void visit_ior(final Instruction inst) { + } + + public void visit_lor(final Instruction inst) { + } + + public void visit_ixor(final Instruction inst) { + } + + public void visit_lxor(final Instruction inst) { + } + + public void visit_iinc(final Instruction inst) { + } + + public void visit_i2l(final Instruction inst) { + } + + public void visit_i2f(final Instruction inst) { + } + + public void visit_i2d(final Instruction inst) { + } + + public void visit_l2i(final Instruction inst) { + } + + public void visit_l2f(final Instruction inst) { + } + + public void visit_l2d(final Instruction inst) { + } + + public void visit_f2i(final Instruction inst) { + } + + public void visit_f2l(final Instruction inst) { + } + + public void visit_f2d(final Instruction inst) { + } + + public void visit_d2i(final Instruction inst) { + } + + public void visit_d2l(final Instruction inst) { + } + + public void visit_d2f(final Instruction inst) { + } + + public void visit_i2b(final Instruction inst) { + } + + public void visit_i2c(final Instruction inst) { + } + + public void visit_i2s(final Instruction inst) { + } + + public void visit_lcmp(final Instruction inst) { + } + + public void visit_fcmpl(final Instruction inst) { + } + + public void visit_fcmpg(final Instruction inst) { + } + + public void visit_dcmpl(final Instruction inst) { + } + + public void visit_dcmpg(final Instruction inst) { + } + + public void visit_ifeq(final Instruction inst) { + } + + public void visit_ifne(final Instruction inst) { + } + + public void visit_iflt(final Instruction inst) { + } + + public void visit_ifge(final Instruction inst) { + } + + public void visit_ifgt(final Instruction inst) { + } + + public void visit_ifle(final Instruction inst) { + } + + public void visit_if_icmpeq(final Instruction inst) { + } + + public void visit_if_icmpne(final Instruction inst) { + } + + public void visit_if_icmplt(final Instruction inst) { + } + + public void visit_if_icmpge(final Instruction inst) { + } + + public void visit_if_icmpgt(final Instruction inst) { + } + + public void visit_if_icmple(final Instruction inst) { + } + + public void visit_if_acmpeq(final Instruction inst) { + } + + public void visit_if_acmpne(final Instruction inst) { + } + + public void visit_goto(final Instruction inst) { + } + + public void visit_jsr(final Instruction inst) { + } + + public void visit_ret(final Instruction inst) { + } + + public void visit_switch(final Instruction inst) { + } + + public void visit_ireturn(final Instruction inst) { + } + + public void visit_lreturn(final Instruction inst) { + } + + public void visit_freturn(final Instruction inst) { + } + + public void visit_dreturn(final Instruction inst) { + } + + public void visit_areturn(final Instruction inst) { + } + + public void visit_return(final Instruction inst) { + } + + public void visit_getstatic(final Instruction inst) { + } + + public void visit_putstatic(final Instruction inst) { + } + + public void visit_putstatic_nowb(final Instruction inst) { + } + + public void visit_getfield(final Instruction inst) { + } + + public void visit_putfield(final Instruction inst) { + } + + public void visit_putfield_nowb(final Instruction inst) { + } + + public void visit_invokevirtual(final Instruction inst) { + } + + public void visit_invokespecial(final Instruction inst) { + } + + public void visit_invokestatic(final Instruction inst) { + } + + public void visit_invokeinterface(final Instruction inst) { + } + + public void visit_new(final Instruction inst) { + } + + public void visit_newarray(final Instruction inst) { + } + + public void visit_arraylength(final Instruction inst) { + } + + public void visit_athrow(final Instruction inst) { + } + + public void visit_checkcast(final Instruction inst) { + } + + public void visit_instanceof(final Instruction inst) { + } + + public void visit_monitorenter(final Instruction inst) { + } + + public void visit_monitorexit(final Instruction inst) { + } + + public void visit_multianewarray(final Instruction inst) { + } + + public void visit_ifnull(final Instruction inst) { + } + + public void visit_ifnonnull(final Instruction inst) { + } + + public void visit_rc(final Instruction inst) { + } + + public void visit_aupdate(final Instruction inst) { + } + + public void visit_supdate(final Instruction inst) { + } + + public void visit_aswizzle(final Instruction inst) { + } + + public void visit_aswrange(final 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..d8d9353 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/InstructionVisitor.java @@ -0,0 +1,330 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * 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 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/Label.java b/src/EDU/purdue/cs/bloat/editor/Label.java new file mode 100644 index 0000000..b2195ad --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Label.java @@ -0,0 +1,141 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * Label is used to label an instruction. Labels are used + * to preserve the location of branch targets. A Label consists of an + * index into the code array and a boolean that determines whether or + * not it starts a basic block. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Label { + public static boolean TRACE = false; + + private int index; + + private boolean startsBlock; + + private String comment; // Comment with Label + + /** + * Constructor. + * + * @param index + * A unique index for the label. For instance, its offset in the + * instruction array. + */ + public Label(final int index) { + this(index, false); + } + + /** + * Constructor. + * + * @param index + * The index of this label into the instruction array + * @param startsBlock + * True if the label is the first instruction in a basic block, + * false if not. + */ + public Label(final int index, final boolean startsBlock) { + this.index = index; + this.startsBlock = startsBlock; + + // if(Label.TRACE) { + // try { + // throw new Exception("Creating a new label: " + this); + // } catch(Exception ex) { + // ex.printStackTrace(System.out); + // } + // } + } + + /** + * Sets the comment associated with this Label. + */ + public void setComment(final String comment) { + this.comment = comment; + } + + /** + * Set if the label starts a block. + * + * @param startsBlock + * True if the label starts a block, false if not. + */ + public void setStartsBlock(final boolean startsBlock) { + this.startsBlock = startsBlock; + } + + /** + * Check if the label starts a block. + * + * @return True if the label starts a block, false if not. + */ + public boolean startsBlock() { + return startsBlock; + } + + /** + * Get the index of the label. + * + * @return The index of the label. + */ + public int index() { + return index; + } + + /** + * Hash the label. + * + * @return The hash code. + */ + public int hashCode() { + return index; + } + + /** + * Check if an object is equal to this label. + * + * @param obj + * The object to compare against. + * @return true if equal, false if not. + */ + public boolean equals(final Object obj) { + return ((obj instanceof Label) && (((Label) obj).index == index)); + } + + /** + * Convert the label to a string. + * + * @return A string representation of the label. + */ + public String toString() { + if (comment != null) { + return "label_" + index + " (" + comment + ")"; + } else { + return "label_" + index; + } + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/LocalVariable.java b/src/EDU/purdue/cs/bloat/editor/LocalVariable.java new file mode 100644 index 0000000..5599211 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/LocalVariable.java @@ -0,0 +1,134 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * LocalVariable represents a local variable index operand to various + * instructions. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class LocalVariable { + private String name; + + private Type type; + + private int index; + + /** + * Constructor. + * + * @param index + * The index of the local variable in the method's local variable + * array. + */ + public LocalVariable(final int index) { + this.name = null; + this.type = null; + this.index = index; + } + + /** + * Constructor. + * + * @param name + * The name of the local variable. + * @param type + * The descriptor (or index into the constant pool) representing + * the variable type. + * @param index + * The index of the local variable in the method's local variable + * array. + */ + public LocalVariable(final String name, final Type type, final int index) { + this.name = name; + this.type = type; + this.index = index; + } + + /** + * Hash the local variable. + * + * A stricter hashing than using the index will break Hashtable lookups + * since a variable could have a name assigned to it after its first use. + * + * @return The hash code. + */ + public int hashCode() { + return index; + } + + /** + * Check if an object is equal to this variable. + * + * A stricter comparison than comparing indices will break Hashtable lookups + * since a variable could have a name assigned to it after its first use. + * + * @param obj + * The object to compare against. + * @return true if equal, false if not. + */ + public boolean equals(final Object obj) { + return (obj != null) && (obj instanceof LocalVariable) + && (((LocalVariable) obj).index == index); + } + + /** + * Get the name of the local variable. + * + * @return The name of the local variable. + */ + public String name() { + return name; + } + + /** + * Get the type of the local variable. + * + * @return The type of the local variable. + */ + public Type type() { + return type; + } + + /** + * Get the index into the local variable array. + * + * @return The index into the local variable array. + */ + public int index() { + return index; + } + + /** + * Convert the variable to a string. + * + * @return A string representation of the variable. + */ + public String toString() { + if (name == null) { + return "Local$" + index; + } + + return name + "$" + index; + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/Makefile b/src/EDU/purdue/cs/bloat/editor/Makefile new file mode 100644 index 0000000..1cb37d1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Makefile @@ -0,0 +1,44 @@ +# 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. +# +# 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 + + +CLASS = \ + ClassEditor.class\ + ClassHierarchy.class\ + CodeArray.class\ + ConstantPool.class\ + EditorContext.class\ + FieldEditor.class\ + IncOperand.class\ + Instruction.class\ + InstructionAdapter.class\ + InstructionVisitor.class\ + Label.class\ + LocalVariable.class\ + MemberRef.class\ + MethodEditor.class\ + MultiArrayOperand.class\ + NameAndType.class\ + Opcode.class\ + SerialVersionUID.class\ + Switch.class\ + TryCatch.class\ + Type.class\ + TypeComparator.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..1502cd4 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/MemberRef.java @@ -0,0 +1,117 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * 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) + */ +public class MemberRef { + private Type declaringClass; + + private 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(final Type declaringClass, final 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 Type 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 + final String className = declaringClass.toString(); + return "<" + (type().isMethod() ? "Method" : "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(final 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/MethodEditor.java b/src/EDU/purdue/cs/bloat/editor/MethodEditor.java new file mode 100644 index 0000000..2afbf07 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/MethodEditor.java @@ -0,0 +1,1757 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * MethodEditor provides a means to edit a method of a class. A + * MethodEditor gathers information from a MethodInfo + * object. It then goes through the bytecodes of the method and extracts + * information about the method. Along the way it creates an array of + * Instruction and Label objects that represent the code. + * Additionally, it models the try-catch blocks in the method and their + * associated exception handlers. + * + * @see EDU.purdue.cs.bloat.reflect.MethodInfo + * @see Label + * @see Instruction + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class MethodEditor implements Opcode { + public static boolean PRESERVE_DEBUG = true; + + public static boolean UNIQUE_HANDLERS = false; + + public static boolean OPT_STACK_2 = false; // byte-code level stack opt + + private ClassEditor editor; // The editor that "owns" this MethodEditor + + private MethodInfo methodInfo; // Representation of this method + + private String name; // The name of this method + + private Type type; // Type variable representing the class's + + // descriptor + private LinkedList code; // Label and Instruction objects representing + // this + + // method's bytecode + private LinkedList tryCatches; // Info about the try-catch blocks in this + // method + + private LinkedList lineNumbers; + + private LocalVariable[] params; // The parameters to this method + + private int maxStack; // Max size of stack while running this method + + private int maxLabel; // Label pointing to the end of the code + + private int maxLocals; // Maximum number of local variables + + private boolean isDirty; // Has the method been modified? + + private Map locals; // Maps indices to that LocalVariable + + private Type[] paramTypes; // Types of parameters (accounts for wides) + + public UseMap uMap; // Structure for remembering use/def info + + private boolean isDeleted = false; + + public MethodEditor(final ClassEditor editor, final int modifiers, + final Class returnType, final String methodName, + final Class[] paramTypes, final Class[] exceptionTypes) { + + this(editor, modifiers, (returnType == null ? null : Type + .getType(returnType)), methodName, MethodEditor + .convertTypes(paramTypes), MethodEditor + .convertTypes(exceptionTypes)); + } + + private static Type[] convertTypes(final Class[] classes) { + if (classes == null) { + return (null); + } + + final Type[] types = new Type[classes.length]; + for (int i = 0; i < types.length; i++) { + types[i] = Type.getType(classes[i]); + } + return (types); + } + + /** + * Creates a new MethodEditor for editing a method in a given + * class with the given modifiers, return type, name, parameter types, and + * exception types. + * + * @param modifiers + * The {@link EDU.purdue.cs.bloat.reflect.Modifiers modifiers} + * for the new method + * @param returnType + * The return type of the method. If, returnType + * is null, the return type is assumed to be void. + * @param methodName + * The name of the method + * @param paramTypes + * The types of the parameters to the new method. If + * paramTypes is null, then we + * assume that there are no arguments. + * @param exceptionTypes + * The types of exceptions that may be thrown by the new method. + * If exceptionTypes is null, then + * we assume that no exceptions are declared. + */ + public MethodEditor(final ClassEditor editor, final int modifiers, + Type returnType, final String methodName, Type[] paramTypes, + Type[] exceptionTypes) { + + // if(ClassEditor.DEBUG) { + // System.out.println("Creating MethodEditor " + + // System.identityHashCode(this)); + // Thread.dumpStack(); + // } + + this.editor = editor; + this.name = methodName; + + if (returnType == null) { + returnType = Type.VOID; + } + + if (paramTypes == null) { + paramTypes = new Type[0]; + } + + if (exceptionTypes == null) { + exceptionTypes = new Type[0]; + } + + // Get the indices in the constant pool for all sorts of + // interesting information + final ConstantPool cp = editor.constants(); + final int nameIndex = cp.getUTF8Index(methodName); + this.type = Type.getType(paramTypes, returnType); + Assert.isTrue(this.type.isMethod(), "Method type not method: " + + this.type); + final int typeIndex = cp.getTypeIndex(this.type); + final int exceptionIndex = cp.getUTF8Index("Exceptions"); + + final int[] exceptionTypeIndices = new int[exceptionTypes.length]; + for (int i = 0; i < exceptionTypes.length; i++) { + final Type eType = exceptionTypes[i]; + exceptionTypeIndices[i] = cp.getTypeIndex(eType); + } + + final int codeIndex = cp.getUTF8Index("Code"); + + final ClassInfo classInfo = editor.classInfo(); + this.methodInfo = classInfo.addNewMethod(modifiers, typeIndex, + nameIndex, exceptionIndex, exceptionTypeIndices, codeIndex); + + // Initialize other parts of this MethodEditor as best we can + this.code = new LinkedList(); + this.tryCatches = new LinkedList(); + this.lineNumbers = new LinkedList(); + this.locals = new HashMap(); + + // Be sure to include space for the this pointer. + if (!isStatic()) { + this.params = new LocalVariable[type.stackHeight() + 1]; + + } else { + this.params = new LocalVariable[type.stackHeight()]; + } + + // Initalize the params to hold LocalVariables representing the + // parameters + this.paramTypes = new Type[this.params.length]; + final Type[] indexedParams = this.type().indexedParamTypes(); + if (!isStatic()) { + // First parameter is the this pointer + this.paramTypes[0] = this.declaringClass().type(); + for (int q = 1; q < this.paramTypes.length; q++) { + this.paramTypes[q] = indexedParams[q - 1]; + } + + } else { + for (int q = 0; q < this.paramTypes.length; q++) { + this.paramTypes[q] = indexedParams[q]; + } + } + + for (int q = 0; q < this.params.length; q++) { + this.params[q] = new LocalVariable(null, this.paramTypes[q], q); + } + + this.maxLocals = this.paramTypes.length; + + this.isDirty = true; + } + + /** + * Constructor. + * + * @param editor + * The class containing the method. + * @param methodInfo + * The method to edit. + * + * @see ClassEditor + * @see EDU.purdue.cs.bloat.reflect.MethodInfo MethodInfo + */ + public MethodEditor(final ClassEditor editor, final MethodInfo methodInfo) { + // if(ClassEditor.DEBUG) { + // System.out.println("Creating MethodEditor " + + // System.identityHashCode(this)); + // Thread.dumpStack(); + // } + + final ConstantPool cp = editor.constants(); + + this.methodInfo = methodInfo; + this.editor = editor; + this.isDirty = false; + + maxLabel = 0; + maxLocals = methodInfo.maxLocals(); + maxStack = methodInfo.maxStack(); + locals = new HashMap(); + + int index; + int i; + int j; + + index = methodInfo.nameIndex(); + name = (String) cp.constantAt(index); + + index = methodInfo.typeIndex(); + final String typeName = (String) cp.constantAt(index); + type = Type.getType(typeName); + + code = new LinkedList(); + tryCatches = new LinkedList(); + lineNumbers = new LinkedList(); + + // Be sure to include space for the this pointer. + if (!isStatic()) { + params = new LocalVariable[type.stackHeight() + 1]; + } else { + params = new LocalVariable[type.stackHeight()]; + } + + // Initalize the params to hold LocalVariables representing the + // parameters + paramTypes = new Type[params.length]; + final Type[] indexedParams = this.type().indexedParamTypes(); + if (!isStatic()) { + // First parameter is the this pointer + paramTypes[0] = this.declaringClass().type(); + for (int q = 1; q < paramTypes.length; q++) { + paramTypes[q] = indexedParams[q - 1]; + } + + } else { + for (int q = 0; q < paramTypes.length; q++) { + paramTypes[q] = indexedParams[q]; + } + } + for (int q = 0; q < params.length; q++) { + params[q] = new LocalVariable(null, paramTypes[q], q); + } + + // Get the byte code for this method + final byte[] array = methodInfo.code(); + + if ((array == null) || (array.length == 0)) { + return; + } + + // Build the array of Instructions (and Labels). + // + // next[i] contains the index of the instruction following i. + // targets[i] contains an array of the branch targets of i. + // lookups[i] contains an array of the switch lookup values of i. + // label[i] contains a label if a label should be inserted before i. + // lines[i] contains the line number of instruction i (or 0). + // + final int[] next = new int[array.length]; + final int[][] targets = new int[array.length][]; + final int[][] lookups = new int[array.length][]; + final Label[] label = new Label[array.length + 1]; + LocalVariable[][] localVars; + + if (MethodEditor.PRESERVE_DEBUG && (array.length < 0x10000)) { + // LocalDebugInfo maps a local variable in the generated code + // back to the name of a local variable in the original Java + // source file. + final LocalDebugInfo[] locals = methodInfo.locals(); + int max = 0; + + // Find the maximum local variable index for the code. + for (i = 0; i < locals.length; i++) { + if (max <= locals[i].index()) { + max = locals[i].index() + 1; + } + } + + // localVars[i][j] contains the a LocalVariable, j, for + // instruction i + localVars = new LocalVariable[array.length][max]; + + // Create LocalVariables for those locals with debug info + // and set the params array so the name and type will be returned + // be paramAt. + // + for (i = 0; i < locals.length; i++) { + final int start = locals[i].startPC(); + final int end = start + locals[i].length(); + + final String localName = (String) cp.constantAt(locals[i] + .nameIndex()); + final String localType = (String) cp.constantAt(locals[i] + .typeIndex()); + + final LocalVariable var = new LocalVariable(localName, Type + .getType(localType), locals[i].index()); + + for (int pc = start; pc <= end; pc++) { + if (pc < localVars.length) { + localVars[pc][locals[i].index()] = var; + } + } + + if ((start == 0) && (locals[i].index() < params.length)) { + params[locals[i].index()] = var; + } + } + + // Create a list of line number entries and add a label at the + // start PC for each entry. + final LineNumberDebugInfo[] lineNumbers = methodInfo.lineNumbers(); + + for (i = 0; i < lineNumbers.length; i++) { + final int start = lineNumbers[i].startPC(); + + if (label[start] == null) { + label[start] = new Label(start, false); + } + + addLineNumberEntry(label[start], lineNumbers[i].lineNumber()); + } + } else { + // We're not preserving debugging information. So, we don't + // need to worry about which local variables are live at + // which instructions. + + localVars = new LocalVariable[array.length][0]; + } + + // Create a label for the beginning of the code and for each + // branch target. Also set next[i] for all instructions i. + // + label[0] = new Label(0, true); + + int numInst = 0; + + for (i = 0; i < array.length; i = next[i]) { + // Examine an instruction and extract its target labels + // and switch lookups + next[i] = munchCode(array, i, targets, lookups); + numInst++; + + // Generate Labels for all the targets local to the code + if (targets[i] != null) { + for (j = 0; j < targets[i].length; j++) { + if (targets[i][j] < array.length) { + label[targets[i][j]] = new Label(targets[i][j], true); + } + } + } + } + + // Create a label for the beginning and end of protected blocks and the + // beginning of catch blocks. Add a TryCatch entry for each + // exception handler in the method. + // + final Catch[] exc = methodInfo.exceptionHandlers(); + + for (i = 0; i < exc.length; i++) { + final int start = exc[i].startPC(); + final int end = exc[i].endPC(); + final int handler = exc[i].handlerPC(); + + label[start] = new Label(start, true); + label[end] = new Label(end, true); + label[handler] = new Label(handler, true); + + final Type catchType = (Type) cp + .constantAt(exc[i].catchTypeIndex()); + + addTryCatch(new TryCatch(label[start], label[end], label[handler], + catchType)); + } + + // Go through the bytecode and create Instructions and build the + // code linked list. + // Add a label for instructions following branches. + for (i = 0; i < array.length; i = next[i]) { + final Instruction inst = new Instruction(array, i, targets[i], + lookups[i], localVars[i], cp); + + if (label[i] != null) { + code.add(label[i]); + } + + code.add(inst); + + if (inst.isJump() || inst.isReturn() || inst.isJsr() + || inst.isRet() || inst.isThrow() || inst.isSwitch()) { + + // Add a label for the next instruction after a branch. + if (next[i] < array.length) { + label[next[i]] = new Label(next[i], true); + } + } + } + + // Add a label at the end. This label must start a block. + label[array.length] = new Label(array.length, true); + code.add(label[array.length]); + + maxLabel = array.length + 1; + + if (ClassEditor.DEBUG) { + System.out.println("Editing method " + name + " " + type); + } + + if (MethodEditor.OPT_STACK_2) { + uMap = new UseMap(); // structure for remembering use/def info. + } + + this.setDirty(false); + } + + /** + * Returns the Types of exceptions that this method may throw. + */ + public Type[] exceptions() { + final ConstantPool cp = editor.constants(); + final int[] indices = methodInfo.exceptionTypes(); + final Type[] types = new Type[indices.length]; + + for (int i = 0; i < indices.length; i++) { + types[i] = (Type) cp.constantAt(indices[i]); + } + + return (types); + } + + /** + * Returns true if this method has been modified. + */ + public boolean isDirty() { + return (this.isDirty); + } + + /** + * Sets the dirty flag of this method. The dirty flag is true if + * the method has been modified. + */ + public void setDirty(final boolean dirty) { + this.isDirty = dirty; + if (isDirty == true) { + this.editor.setDirty(true); + } + } + + /** + * Marks this method for deletion. Once a method has been marked for + * deletion all attempts to change it will throw an + * IllegalStateException. + */ + public void delete() { + this.setDirty(true); + this.isDeleted = true; + } + + /** + * Returns an array of Types representing the types of the + * parameters of this method. It's really used to figure out the type of the + * local variables that hold the parameters. So, wide data is succeeded by + * an empty slot. Also, for virtual methods, the first element in the array + * is the receiver. + */ + public Type[] paramTypes() { + return (this.paramTypes); + } + + /** + * Get the LocalVariable for the parameter at the given index. + * + * @param index + * The index into the params (0 is the this pointer or the first + * argument, if static). + * @return The LocalVariable for the parameter at the given index. + * + */ + public LocalVariable paramAt(final int index) { + if ((index >= params.length) || (params[index] == null)) { + final LocalVariable local = new LocalVariable(index); + if (index < params.length) { + params[index] = local; + } + return (local); + } + + return params[index]; + } + + /** + * Returns the raw MethodInfo of the method being edited. + */ + public MethodInfo methodInfo() { + return methodInfo; + } + + /** + * Returns the class which declared the method. + */ + public ClassEditor declaringClass() { + return editor; + } + + /** + * Returns the maximum number of locals used by the method. + */ + public int maxLocals() { + return maxLocals; + } + + public boolean isPublic() { + return (methodInfo.modifiers() & Modifiers.PUBLIC) != 0; + } + + public boolean isPrivate() { + return (methodInfo.modifiers() & Modifiers.PRIVATE) != 0; + } + + public boolean isProtected() { + return (methodInfo.modifiers() & Modifiers.PROTECTED) != 0; + } + + /** + * Returns true is the method has package level visibility + */ + public boolean isPackage() { + return (!isPublic() && !isPrivate() && !isProtected()); + } + + public boolean isStatic() { + return (methodInfo.modifiers() & Modifiers.STATIC) != 0; + } + + public boolean isFinal() { + return (methodInfo.modifiers() & Modifiers.FINAL) != 0; + } + + public boolean isSynchronized() { + return (methodInfo.modifiers() & Modifiers.SYNCHRONIZED) != 0; + } + + public boolean isNative() { + return (methodInfo.modifiers() & Modifiers.NATIVE) != 0; + } + + public boolean isAbstract() { + return (methodInfo.modifiers() & Modifiers.ABSTRACT) != 0; + } + + /** + * Returns true if this method's class is an interface. + */ + public boolean isInterface() { + return (editor.isInterface()); + } + + // TODO: Only change the methodInfo at commit time. + // TODO: Add similar methods to field and class editors. + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setPublic(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.PUBLIC; + } else { + mod &= ~Modifiers.PUBLIC; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setPrivate(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.PRIVATE; + } else { + mod &= ~Modifiers.PRIVATE; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setProtected(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.PROTECTED; + } else { + mod &= ~Modifiers.PROTECTED; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setStatic(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.STATIC; + } else { + mod &= ~Modifiers.STATIC; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setFinal(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.FINAL; + } else { + mod &= ~Modifiers.FINAL; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setSynchronized(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.SYNCHRONIZED; + } else { + mod &= ~Modifiers.SYNCHRONIZED; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setNative(final boolean flag) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.NATIVE; + } else { + mod &= ~Modifiers.NATIVE; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + public void setAbstract(final boolean flag) { + int mod = methodInfo.modifiers(); + + if (flag) { + mod |= Modifiers.ABSTRACT; + } else { + mod &= ~Modifiers.ABSTRACT; + } + + methodInfo.setModifiers(mod); + this.setDirty(true); + } + + /** + * Scan the raw bytes of a single instruction, saving the indices of branch + * targets and the values of switch lookups. That is, gather information + * needed for creating Instruction instances. + * + * @param code + * The byte code array. + * @param index + * The index into the code array. + * @param targets + * Branch targets for the instruction scanned. This is set by the + * method. + * @param lookups + * Switch lookups for the instruction scanned. This is set by the + * method. + * @return The index of the next instruction in the code array. + */ + private int munchCode(final byte[] code, final int index, + final int[][] targets, final int[][] lookups) { + final int opcode = Instruction.toUByte(code[index]); + int next = index + Opcode.opcSize[opcode]; + + switch (opcode) { + case opc_ifeq: + case opc_ifne: + case opc_iflt: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmplt: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: { + // Branch target + final int target = Instruction.toShort(code[index + 1], + code[index + 2]); + targets[index] = new int[1]; + targets[index][0] = index + target; + break; + } + case opc_goto: + case opc_jsr: { + // Branch target + final int target = Instruction.toShort(code[index + 1], + code[index + 2]); + targets[index] = new int[1]; + targets[index][0] = index + target; + break; + } + case opc_goto_w: + case opc_jsr_w: { + // Branch target + final int target = Instruction.toInt(code[index + 1], + code[index + 2], code[index + 3], code[index + 4]); + targets[index] = new int[1]; + targets[index][0] = index + target; + break; + } + case opc_ret: { + // Unconditional branch to the address in a local variable. + // Work on finding branch targets later. + break; + } + case opc_tableswitch: { + int target; + int lo; + int hi; + int j; + + // The targets and low and high values are aligned on + // 4-byte boundaries. + for (j = index + 1; j % 4 != 0; j++) { + // Empty statement. + } + + // Read the default target. + target = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + lo = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + hi = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + lookups[index] = new int[2]; + lookups[index][0] = lo; + lookups[index][1] = hi; + + targets[index] = new int[hi - lo + 2]; + + int k = 0; + targets[index][k++] = index + target; + + next = j + (hi - lo + 1) * 4; + + while (j < next) { + target = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + targets[index][k++] = index + target; + } + + break; + } + case opc_lookupswitch: { + int target; + int value; + int npairs; + int j; + + // The targets and pairs are aligned on 4-byte boundaries. + for (j = index + 1; j % 4 != 0; j++) { + // Empty statement. + } + + // Read the default target. + target = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + npairs = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + lookups[index] = new int[npairs]; + targets[index] = new int[npairs + 1]; + + int k = 0; + targets[index][k++] = index + target; + + next = j + npairs * 8; + + while (j < next) { + value = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + target = Instruction.toInt(code[j], code[j + 1], code[j + 2], + code[j + 3]); + j += 4; + + lookups[index][k - 1] = value; + targets[index][k++] = index + target; + } + + break; + } + case opc_wide: { + if (code[index + 1] == (byte) Opcode.opc_iinc) { + next = index + 6; + } else { + next = index + 4; + } + break; + } + } + + return next; + } + + /** + * Remove all the instructions in preparation for the instructions being + * added back after a control flow graph edit. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void clearCode() { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + if (ClassEditor.DEBUG) { + System.out.println("Clearing code"); + Thread.dumpStack(); + } + + code.clear(); + tryCatches.clear(); + maxLocals = 0; + maxStack = 0; + this.setDirty(true); + } + + /** + * Like clear code, but doesn't reset the maxLocals. I'm not really sure why + * this works, but it stops certain parts of code that is generated and then + * re-cfg'd from being eliminated as dead + */ + + public void clearCode2() { + code.clear(); + tryCatches.clear(); + maxStack = 0; + this.setDirty(true); + } + + /** + * Returns the name of the method. + */ + public String name() { + return name; + } + + /** + * Returns true if the method being edited is a constructor. + */ + public boolean isConstructor() { + return (name.equals("")); + } + + /** + * Returns the type of the method. + */ + public Type type() { + return type; + } + + /** + * Returns the NameAndType of the method. + */ + public NameAndType nameAndType() { + return (new NameAndType(this.name(), this.type())); + } + + /** + * Returns a MemberRef for the method. + */ + public MemberRef memberRef() { + return (new MemberRef(this.declaringClass().type(), this.nameAndType())); + } + + /** + * Get the length of the code array. + * + * @return The length of the code array. + */ + public int codeLength() { + return code.size(); + } + + /** + * @throws IllegalStateException This field has been marked for deletion + */ + public void setCode(final List v) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + if (ClassEditor.DEBUG) { + System.out.println("Setting code to " + v.size() + " instructions"); + Thread.dumpStack(); + } + code = new LinkedList(v); + this.setDirty(true); + } + + /** + * Returns the code (Instructions and Labels) in + * the method. + */ + public List code() { + return code; + } + + /** + * Get the label of the first block. + */ + public Label firstBlock() { + final Iterator iter = code.iterator(); + + while (iter.hasNext()) { + final Object obj = iter.next(); + + if (obj instanceof Label) { + final Label l = (Label) obj; + if (l.startsBlock()) { + return l; + } + } + } + + return null; + } + + /** + * Get the label of the next block after the parameter. + * + * @param label + * The label at which to begin. + * @return The label. + */ + public Label nextBlock(final Label label) { + boolean seen = false; + + final Iterator iter = code.iterator(); + + while (iter.hasNext()) { + final Object obj = iter.next(); + + if (obj instanceof Label) { + if (seen) { + final Label l = (Label) obj; + if (l.startsBlock()) { + return l; + } + } else if (label.equals(obj)) { + seen = true; + } + } + } + + return null; + } + + /** + * Removes a Label or Instruction from the code array. + * + * @param i + * The index of the element to remove. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void removeCodeAt(final int i) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + code.remove(i); + this.setDirty(true); + } + + /** + * Inserts a Label or Instruction into the code array. + * + * @param i + * The index of the element to insert before. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void insertCodeAt(final Object obj, final int i) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + code.add(i, obj); + this.setDirty(true); + } + + /** + * Replace a Label or Instruction in the code array. + * + * @param obj + * The new element. + * @param i + * The index of the element to replace + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void replaceCodeAt(final Object obj, final int i) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + code.set(i, obj); + this.setDirty(true); + } + + /** + * Returns a Label or Instruction in the code array. + * + * @param i + * The index into the code array. + * @return The element at the index. + */ + public Object codeElementAt(final int i) { + return code.get(i); + } + + /** + * Add a line number entry. + * + * @param label + * The label beginning the range of instructions for this line + * number. + * @param lineNumber + * The line number. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void addLineNumberEntry(final Label label, final int lineNumber) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + lineNumbers.add(new LineNumberEntry(label, lineNumber)); + this.setDirty(true); + } + + /** + * Returns the number of exception handlers in the method. + */ + public int numTryCatches() { + return tryCatches.size(); + } + + /** + * Returns the exception handlers (TryCatch) in the method. + */ + public Collection tryCatches() { + return tryCatches; + } + + /** + * Add an exception handler. + * + * @param tryCatch + * An exception handler. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void addTryCatch(final TryCatch tryCatch) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + if (ClassEditor.DEBUG) { + System.out.println("add " + tryCatch); + } + + tryCatches.add(tryCatch); + this.setDirty(true); + } + + class LineNumberEntry { + Label label; + + int lineNumber; + + public LineNumberEntry(final Label label, final int lineNumber) { + this.label = label; + this.lineNumber = lineNumber; + } + } + + class LocalInfo { + LocalVariable var; + + Type type; + + public LocalInfo(final LocalVariable var, final Type type) { + this.var = var; + this.type = type; + } + + public boolean equals(final Object obj) { + return (obj != null) && (obj instanceof LocalInfo) + && ((LocalInfo) obj).var.equals(var) + && ((LocalInfo) obj).type.equals(type); + } + + public int hashCode() { + return var.hashCode() ^ type.hashCode(); + } + } + + /** + * Creates a new local variable. + */ + public LocalVariable newLocal(final Type type) { + final int index = maxLocals; + + maxLocals += type.stackHeight(); + this.setDirty(true); + + final LocalVariable local = new LocalVariable(index); + + locals.put(new Integer(index), local); + + return (local); + } + + /** + * Creates a new local variable of an undertermined type. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public LocalVariable newLocal(final boolean isWide) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + final int index = maxLocals; + + maxLocals += (isWide ? 2 : 1); + this.setDirty(true); + + final LocalVariable local = new LocalVariable(index); + + locals.put(new Integer(index), local); + + return (local); + } + + /** + * Returns the LocalVariable with the given index. If there is no + * local variable at that index, a new one is created at that index. We + * assume that this variable is not wide. + */ + public LocalVariable localAt(int index) { + LocalVariable local = (LocalVariable) locals.get(new Integer(index)); + + if (local == null) { + local = new LocalVariable(index); + locals.put(new Integer(index), local); + if (index >= maxLocals) { + maxLocals = index++; // Dangerous? + } + } + + return (local); + } + + /** + * Add an instruction. + * + * @param opcodeClass + * The instruction to add. + */ + public void addInstruction(final int opcodeClass) { + addInstruction(new Instruction(opcodeClass)); + } + + /** + * Add an instruction. + * + * @param opcodeClass + * The instruction to add. + */ + public void addInstruction(final int opcodeClass, final Object operand) { + addInstruction(new Instruction(opcodeClass, operand)); + } + + /** + * Add an instruction to the end of the code array. + * + * @param inst + * The instruction to add. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void addInstruction(final Instruction inst) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + if (ClassEditor.DEBUG) { + System.out.println(" " + inst + " to " + + System.identityHashCode(this) + ":" + + System.identityHashCode(this.code)); + } + + code.add(inst); + this.setDirty(true); + } + + /** + * Get the next available label. That is the Label after the final + * Instruction in the code array. + * + * @return A new label. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public Label newLabel() { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + this.setDirty(true); + return new Label(maxLabel++); + } + + public Label newLabelTrue() { + return new Label(maxLabel++, true); + } + + /** + * Add a label to the code array to the end of the code array. + * + * @param label + * The label to add. + * + * @throws IllegalStateException This field has been marked for deletion + */ + public void addLabel(final Label label) { + if (this.isDeleted) { + final String s = "Cannot change a field once it has been marked " + + "for deletion"; + throw new IllegalStateException(s); + } + + if (ClassEditor.DEBUG) { + System.out.println(" " + label + " to " + + System.identityHashCode(this) + ":" + + System.identityHashCode(this.code)); + } + + if (label.index() >= maxLabel) { + maxLabel = label.index() + 1; + } + + code.add(label); + this.setDirty(true); + } + + class LocalVarEntry { + LocalVariable var; + + Label start; + + Label end; + + public LocalVarEntry(final LocalVariable var, final Label start, + final Label end) { + this.var = var; + this.start = start; + this.end = end; + } + } + + /** + * Commits changes made to this MethodEditor back to the MethodInfo on which + * it is based. Note that committal will take place regardless of whether or + * not the method is dirty. + */ + public void commit() { + if (ClassEditor.DEBUG) { + System.out.println("Committing method " + this.name + " " + + this.type + ": " + this.code.size() + " insts " + + System.identityHashCode(this) + ":" + + System.identityHashCode(this.code)); + } + + final ConstantPool cp = editor.constants(); + + if (this.isDeleted) { + final int nameIndex = cp.getUTF8Index(this.name); + final int typeIndex = cp.getTypeIndex(this.type); + this.editor.classInfo().deleteMethod(nameIndex, typeIndex); + + } else { + methodInfo.setNameIndex(cp.addConstant(Constant.UTF8, name)); + methodInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type + .descriptor())); + + if (isNative() || isAbstract()) { + return; + } + + final List vars = new ArrayList(); + final List copy = new LinkedList(); + + Iterator iter = code.iterator(); + + CODE: while (iter.hasNext()) { + final Object ce = iter.next(); + + if (ce instanceof Label) { + copy.add(ce); + continue CODE; + } + + final Instruction inst = (Instruction) ce; + + LocalVariable var = null; + + if (inst.operand() instanceof LocalVariable) { + var = (LocalVariable) inst.operand(); + } else if (inst.operand() instanceof IncOperand) { + var = ((IncOperand) inst.operand()).var(); + } + + if ((var == null) || (var.name() == null) + || (var.type() == null)) { + copy.add(ce); + continue CODE; + } + + for (int j = vars.size() - 1; j >= 0; j--) { + final LocalVarEntry v = (LocalVarEntry) vars.get(j); + + // Same variable, extend the range of the variable and + // go to the next instruction. + if (v.var.equals(var)) { + v.end = newLabel(); + + // Add a label after the instruction. + copy.add(ce); + copy.add(v.end); + continue CODE; + } + + // Different variable, same index. We have to add an entry + // for the variable since we know the live range for this + // index starts here. + if (v.var.index() == var.index()) { + break; + } + } + + final Label start = newLabel(); + final Label end = newLabel(); + + vars.add(new LocalVarEntry(var, start, end)); + + // Add labels before and after the instruction. + copy.add(start); + copy.add(ce); + copy.add(end); + } + + final HashSet seen = new HashSet(); + final ArrayList dup = new ArrayList(); + + iter = tryCatches.iterator(); + + while (iter.hasNext()) { + final TryCatch tc = (TryCatch) iter.next(); + + if (!seen.contains(tc.handler())) { + if (ClassEditor.DEBUG) { + System.out.println("See " + tc.handler()); + } + seen.add(tc.handler()); + } else { + if (ClassEditor.DEBUG) { + System.out.println("See " + tc.handler() + " again"); + } + dup.add(tc); + } + } + + if (dup.size() != 0) { + final ListIterator liter = copy.listIterator(); + + while (liter.hasNext()) { + final Object ce = liter.next(); + + if (ce instanceof Label) { + final Iterator d = dup.iterator(); + + while (d.hasNext()) { + final TryCatch tc = (TryCatch) d.next(); + + if (tc.handler().equals(ce)) { + // Split the exception handler. + // + // Handler: + // nop <-- nop needed to prevent TowerJ + // goto L2 from removing the goto + // Handler2: + // nop + // goto L2 + // Code: + // handler code + + Instruction jump; + Instruction nop; + + final Label handler2 = newLabel(); + final Label code = newLabel(); + + nop = new Instruction(Opcode.opcx_nop); + liter.add(nop); + + jump = new Instruction(Opcode.opcx_goto, code); + liter.add(jump); + + liter.add(handler2); + + nop = new Instruction(Opcode.opcx_nop); + liter.add(nop); + + jump = new Instruction(Opcode.opcx_goto, code); + liter.add(jump); + + liter.add(code); + + if (ClassEditor.DEBUG) { + System.out.println("Insert " + jump); + System.out.println("Insert " + handler2); + System.out.println("Insert " + jump); + System.out.println("Insert " + code); + } + + tc.setHandler(handler2); + + d.remove(); + } + } + } + } + } + + final CodeArray array = new CodeArray(this, cp, copy); + final byte[] arr = array.array(); + + methodInfo.setCode(arr); + + methodInfo.setMaxLocals(array.maxLocals()); + methodInfo.setMaxStack(array.maxStack()); + + if (MethodEditor.PRESERVE_DEBUG && (arr.length < 0x10000)) { + final LocalDebugInfo[] locals = new LocalDebugInfo[vars.size()]; + + for (int i = 0; i < vars.size(); i++) { + final LocalVarEntry entry = (LocalVarEntry) vars.get(i); + + final int start = array.labelIndex(entry.start); + final int end = array.labelIndex(entry.end); + + if (start < end) { + locals[i] = new LocalDebugInfo(start, end - start, cp + .addConstant(Constant.UTF8, entry.var.name()), + cp.addConstant(Constant.UTF8, entry.var.type() + .descriptor()), entry.var.index()); + } + } + + methodInfo.setLocals(locals); + + final LineNumberDebugInfo[] lines = new LineNumberDebugInfo[lineNumbers + .size()]; + int i = 0; + + iter = lineNumbers.iterator(); + + while (iter.hasNext()) { + final LineNumberEntry line = (LineNumberEntry) iter.next(); + lines[i++] = new LineNumberDebugInfo(array + .labelIndex(line.label), line.lineNumber); + } + + methodInfo.setLineNumbers(lines); + } else { + methodInfo.setLineNumbers(null); + methodInfo.setLocals(null); + } + + final List c = new LinkedList(); + + iter = tryCatches.iterator(); + + while (iter.hasNext()) { + final TryCatch tc = (TryCatch) iter.next(); + + final int start = array.labelIndex(tc.start()); + final int end = array.labelIndex(tc.end()); + + if (start < end) { + c.add(new Catch(start, end, array.labelIndex(tc.handler()), + cp.addConstant(Constant.CLASS, tc.type()))); + } + } + + final Object[] a = c.toArray(); + final Catch[] catches = new Catch[a.length]; + System.arraycopy(a, 0, catches, 0, a.length); + + methodInfo.setExceptionHandlers(catches); + + } + + if (ClassEditor.DEBUG) { + System.out.println("MethodInfo after commit: " + methodInfo); + } + + // Method is no longer dirty + this.isDirty = false; + } + + /** + * Print the method. + * + * @param out + * Stream to which to print. + */ + public void print(final PrintStream out) { + out.println(name + "." + type + (isDirty ? " (dirty) " : "") + ":"); + + Iterator iter; + + iter = code.iterator(); + + while (iter.hasNext()) { + out.println(" " + iter.next()); + } + + iter = tryCatches.iterator(); + + while (iter.hasNext()) { + out.println(" " + iter.next()); + } + } + + /** + * Two MethodEditors are equal if they edit the same method in + * the same class. + */ + public boolean equals(final Object o) { + if (o instanceof MethodEditor) { + final MethodEditor other = (MethodEditor) o; + + if (!other.declaringClass().equals(this.declaringClass())) { + return (false); + } + if (!other.name().equals(this.name())) { + return (false); + } + if (!other.type().equals(this.type())) { + return (false); + } + + return (true); + } + + return (false); + } + + /** + * A MethodEditor's hash code is based on the hash codes for its + * class, name, and type. + */ + public int hashCode() { + return (this.declaringClass().hashCode() + this.name().hashCode() + this + .type().hashCode()); + } + + public String toString() { + return (editor.type() + "." + name + type); + } + + public UseMap uMap() { + return uMap; + } + + public void rememberDef(final LocalExpr e) { + if (MethodEditor.OPT_STACK_2) { + uMap.add(e, (Instruction) code.get(code.size() - 1)); + } + } + +} diff --git a/src/EDU/purdue/cs/bloat/editor/MultiArrayOperand.java b/src/EDU/purdue/cs/bloat/editor/MultiArrayOperand.java new file mode 100644 index 0000000..9fde1a5 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/MultiArrayOperand.java @@ -0,0 +1,76 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * MultiArrayOperand encapsulates the operands to the + * multianewarray instruction. Each MultiArrayOperand + * contains the type descriptor of the new multidimensional array the + * instruction creates, as well as the number of dimensions in the array. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class MultiArrayOperand { + private Type type; + + private int dim; + + /** + * Constructor. + * + * @param type + * The element type of the array. + * @param dim + * The number of dimensions of the array. + */ + public MultiArrayOperand(final Type type, final int dim) { + this.type = type; + this.dim = dim; + } + + /** + * Get the element type of the array. + * + * @return The element type of the array. + */ + public Type type() { + return type; + } + + /** + * Get the number of dimensions of the array. + * + * @return The number of dimensions of the array. + */ + public int dimensions() { + return dim; + } + + /** + * Convert the operand to a string. + * + * @return A string representation of the operand. + */ + public String toString() { + return type + " x " + dim; + } +} 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..ba6a38f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/NameAndType.java @@ -0,0 +1,83 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * Methods and fields are described by their name and type descriptor. + * NameAndType represents exactly that. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class NameAndType { + private String name; + + private Type type; + + /** + * Constructor. + */ + public NameAndType(final String name, final 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(final 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/Opcode.java b/src/EDU/purdue/cs/bloat/editor/Opcode.java new file mode 100644 index 0000000..b92795b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Opcode.java @@ -0,0 +1,1766 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * Opcode is an interface containing constants defining the opcodes of + * instructions and related constants. + *

    + *
  • opc_XXX are the opcodes. + * + *
  • opcx_XXX are the opcode classes. These are used externally by + * Instruction. + * + *
  • opcNames is an array of opcode names, indexed by the opcode. + * + *
  • opcSize is an array of the bytecode instruction lengths, indexed by the + * opcode. + * + *
  • opcXMap is an array, indexed by the opcode, mapping opcodes to opcode + * classes. + *
+ * + * @see Instruction + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface Opcode { + // Extended opcodes + public static final int opc_nop = 0; + + public static final int opc_aconst_null = 1; + + public static final int opc_iconst_m1 = 2; + + public static final int opc_iconst_0 = 3; + + public static final int opc_iconst_1 = 4; + + public static final int opc_iconst_2 = 5; + + public static final int opc_iconst_3 = 6; + + public static final int opc_iconst_4 = 7; + + public static final int opc_iconst_5 = 8; + + public static final int opc_lconst_0 = 9; + + public static final int opc_lconst_1 = 10; + + public static final int opc_fconst_0 = 11; + + public static final int opc_fconst_1 = 12; + + public static final int opc_fconst_2 = 13; + + public static final int opc_dconst_0 = 14; + + public static final int opc_dconst_1 = 15; + + public static final int opc_bipush = 16; + + public static final int opc_sipush = 17; + + public static final int opc_ldc = 18; + + public static final int opc_ldc_w = 19; + + public static final int opc_ldc2_w = 20; + + public static final int opc_iload = 21; + + public static final int opc_lload = 22; + + public static final int opc_fload = 23; + + public static final int opc_dload = 24; + + public static final int opc_aload = 25; + + public static final int opc_iload_0 = 26; + + public static final int opc_iload_1 = 27; + + public static final int opc_iload_2 = 28; + + public static final int opc_iload_3 = 29; + + public static final int opc_lload_0 = 30; + + public static final int opc_lload_1 = 31; + + public static final int opc_lload_2 = 32; + + public static final int opc_lload_3 = 33; + + public static final int opc_fload_0 = 34; + + public static final int opc_fload_1 = 35; + + public static final int opc_fload_2 = 36; + + public static final int opc_fload_3 = 37; + + public static final int opc_dload_0 = 38; + + public static final int opc_dload_1 = 39; + + public static final int opc_dload_2 = 40; + + public static final int opc_dload_3 = 41; + + public static final int opc_aload_0 = 42; + + public static final int opc_aload_1 = 43; + + public static final int opc_aload_2 = 44; + + public static final int opc_aload_3 = 45; + + public static final int opc_iaload = 46; + + public static final int opc_laload = 47; + + public static final int opc_faload = 48; + + public static final int opc_daload = 49; + + public static final int opc_aaload = 50; + + public static final int opc_baload = 51; + + public static final int opc_caload = 52; + + public static final int opc_saload = 53; + + public static final int opc_istore = 54; + + public static final int opc_lstore = 55; + + public static final int opc_fstore = 56; + + public static final int opc_dstore = 57; + + public static final int opc_astore = 58; + + public static final int opc_istore_0 = 59; + + public static final int opc_istore_1 = 60; + + public static final int opc_istore_2 = 61; + + public static final int opc_istore_3 = 62; + + public static final int opc_lstore_0 = 63; + + public static final int opc_lstore_1 = 64; + + public static final int opc_lstore_2 = 65; + + public static final int opc_lstore_3 = 66; + + public static final int opc_fstore_0 = 67; + + public static final int opc_fstore_1 = 68; + + public static final int opc_fstore_2 = 69; + + public static final int opc_fstore_3 = 70; + + public static final int opc_dstore_0 = 71; + + public static final int opc_dstore_1 = 72; + + public static final int opc_dstore_2 = 73; + + public static final int opc_dstore_3 = 74; + + public static final int opc_astore_0 = 75; + + public static final int opc_astore_1 = 76; + + public static final int opc_astore_2 = 77; + + public static final int opc_astore_3 = 78; + + public static final int opc_iastore = 79; + + public static final int opc_lastore = 80; + + public static final int opc_fastore = 81; + + public static final int opc_dastore = 82; + + public static final int opc_aastore = 83; + + public static final int opc_bastore = 84; + + public static final int opc_castore = 85; + + public static final int opc_sastore = 86; + + public static final int opc_pop = 87; + + public static final int opc_pop2 = 88; + + public static final int opc_dup = 89; + + public static final int opc_dup_x1 = 90; + + public static final int opc_dup_x2 = 91; + + public static final int opc_dup2 = 92; + + public static final int opc_dup2_x1 = 93; + + public static final int opc_dup2_x2 = 94; + + public static final int opc_swap = 95; + + public static final int opc_iadd = 96; + + public static final int opc_ladd = 97; + + public static final int opc_fadd = 98; + + public static final int opc_dadd = 99; + + public static final int opc_isub = 100; + + public static final int opc_lsub = 101; + + public static final int opc_fsub = 102; + + public static final int opc_dsub = 103; + + public static final int opc_imul = 104; + + public static final int opc_lmul = 105; + + public static final int opc_fmul = 106; + + public static final int opc_dmul = 107; + + public static final int opc_idiv = 108; + + public static final int opc_ldiv = 109; + + public static final int opc_fdiv = 110; + + public static final int opc_ddiv = 111; + + public static final int opc_irem = 112; + + public static final int opc_lrem = 113; + + public static final int opc_frem = 114; + + public static final int opc_drem = 115; + + public static final int opc_ineg = 116; + + public static final int opc_lneg = 117; + + public static final int opc_fneg = 118; + + public static final int opc_dneg = 119; + + public static final int opc_ishl = 120; + + public static final int opc_lshl = 121; + + public static final int opc_ishr = 122; + + public static final int opc_lshr = 123; + + public static final int opc_iushr = 124; + + public static final int opc_lushr = 125; + + public static final int opc_iand = 126; + + public static final int opc_land = 127; + + public static final int opc_ior = 128; + + public static final int opc_lor = 129; + + public static final int opc_ixor = 130; + + public static final int opc_lxor = 131; + + public static final int opc_iinc = 132; + + public static final int opc_i2l = 133; + + public static final int opc_i2f = 134; + + public static final int opc_i2d = 135; + + public static final int opc_l2i = 136; + + public static final int opc_l2f = 137; + + public static final int opc_l2d = 138; + + public static final int opc_f2i = 139; + + public static final int opc_f2l = 140; + + public static final int opc_f2d = 141; + + public static final int opc_d2i = 142; + + public static final int opc_d2l = 143; + + public static final int opc_d2f = 144; + + public static final int opc_i2b = 145; + + public static final int opc_i2c = 146; + + public static final int opc_i2s = 147; + + public static final int opc_lcmp = 148; + + public static final int opc_fcmpl = 149; + + public static final int opc_fcmpg = 150; + + public static final int opc_dcmpl = 151; + + public static final int opc_dcmpg = 152; + + public static final int opc_ifeq = 153; + + public static final int opc_ifne = 154; + + public static final int opc_iflt = 155; + + public static final int opc_ifge = 156; + + public static final int opc_ifgt = 157; + + public static final int opc_ifle = 158; + + public static final int opc_if_icmpeq = 159; + + public static final int opc_if_icmpne = 160; + + public static final int opc_if_icmplt = 161; + + public static final int opc_if_icmpge = 162; + + public static final int opc_if_icmpgt = 163; + + public static final int opc_if_icmple = 164; + + public static final int opc_if_acmpeq = 165; + + public static final int opc_if_acmpne = 166; + + public static final int opc_goto = 167; + + public static final int opc_jsr = 168; + + public static final int opc_ret = 169; + + public static final int opc_tableswitch = 170; + + public static final int opc_lookupswitch = 171; + + public static final int opc_ireturn = 172; + + public static final int opc_lreturn = 173; + + public static final int opc_freturn = 174; + + public static final int opc_dreturn = 175; + + public static final int opc_areturn = 176; + + public static final int opc_return = 177; + + public static final int opc_getstatic = 178; + + public static final int opc_putstatic = 179; + + public static final int opc_getfield = 180; + + public static final int opc_putfield = 181; + + public static final int opc_invokevirtual = 182; + + public static final int opc_invokespecial = 183; + + public static final int opc_invokestatic = 184; + + public static final int opc_invokeinterface = 185; + + public static final int opc_xxxunusedxxx = 186; + + public static final int opc_new = 187; + + public static final int opc_newarray = 188; + + public static final int opc_anewarray = 189; + + public static final int opc_arraylength = 190; + + public static final int opc_athrow = 191; + + public static final int opc_checkcast = 192; + + public static final int opc_instanceof = 193; + + public static final int opc_monitorenter = 194; + + public static final int opc_monitorexit = 195; + + public static final int opc_wide = 196; + + public static final int opc_multianewarray = 197; + + public static final int opc_ifnull = 198; + + public static final int opc_ifnonnull = 199; + + public static final int opc_goto_w = 200; + + public static final int opc_jsr_w = 201; + + public static final int opc_breakpoint = 202; + + // Opcodes for persistence + public static final int opc_rc = 237; // residency check + + public static final int opc_aupdate = 238; // pointer update check + + public static final int opc_supdate = 239; // scalar update check + + public static final int opc_aswizzle = 240; // array swizzle check + + public static final int opc_aswrange = 241; // array range swizzle + + public static final int opc_putfield_nowb = 204; + + public static final int opc_putstatic_nowb = 205; + + // Opcode classes (similar opcodes are squeezed together). + + /** + * Opcode class for nop, xxxunusedxxx, wide, breakpoint, and opcodes + * 203-254. + */ + public static final int opcx_nop = Opcode.opc_nop; + + /** + * Opcode class for aconst_null, iconst_m1, iconst_0, iconst_1, iconst_2, + * iconst_3, iconst_4, iconst_5, lconst_0, lconst_1, fconst_0, fconst_1, + * fconst_2, dconst_0, dconst_1, bipush, sipush, ldc, ldc_w, ldc2_w. + */ + public static final int opcx_ldc = Opcode.opc_ldc; + + /** + * Opcode class for iload, iload_0, iload_1, iload_2, iload_3. + */ + public static final int opcx_iload = Opcode.opc_iload; + + /** + * Opcode class for lload, lload_0, lload_1, lload_2, lload_3. + */ + public static final int opcx_lload = Opcode.opc_lload; + + /** + * Opcode class for fload, fload_0, fload_1, fload_2, fload_3. + */ + public static final int opcx_fload = Opcode.opc_fload; + + /** + * Opcode class for dload, dload_0, dload_1, dload_2, dload_3. + */ + public static final int opcx_dload = Opcode.opc_dload; + + /** + * Opcode class for aload, aload_0, aload_1, aload_2, aload_3. + */ + public static final int opcx_aload = Opcode.opc_aload; + + /** + * Opcode class for iaload. + */ + public static final int opcx_iaload = Opcode.opc_iaload; + + /** + * Opcode class for laload. + */ + public static final int opcx_laload = Opcode.opc_laload; + + /** + * Opcode class for faload. + */ + public static final int opcx_faload = Opcode.opc_faload; + + /** + * Opcode class for daload. + */ + public static final int opcx_daload = Opcode.opc_daload; + + /** + * Opcode class for aaload. + */ + public static final int opcx_aaload = Opcode.opc_aaload; + + /** + * Opcode class for baload. + */ + public static final int opcx_baload = Opcode.opc_baload; + + /** + * Opcode class for caload. + */ + public static final int opcx_caload = Opcode.opc_caload; + + /** + * Opcode class for saload. + */ + public static final int opcx_saload = Opcode.opc_saload; + + /** + * Opcode class for istore, istore_0, istore_1, istore_2, istore_3. + */ + public static final int opcx_istore = Opcode.opc_istore; + + /** + * Opcode class for lstore, lstore_0, lstore_1, lstore_2, lstore_3. + */ + public static final int opcx_lstore = Opcode.opc_lstore; + + /** + * Opcode class for fstore, fstore_0, fstore_1, fstore_2, fstore_3. + */ + public static final int opcx_fstore = Opcode.opc_fstore; + + /** + * Opcode class for dstore, dstore_0, dstore_1, dstore_2, dstore_3. + */ + public static final int opcx_dstore = Opcode.opc_dstore; + + /** + * Opcode class for astore, astore_0, astore_1, astore_2, astore_3. + */ + public static final int opcx_astore = Opcode.opc_astore; + + /** + * Opcode class for iastore. + */ + public static final int opcx_iastore = Opcode.opc_iastore; + + /** + * Opcode class for lastore. + */ + public static final int opcx_lastore = Opcode.opc_lastore; + + /** + * Opcode class for fastore. + */ + public static final int opcx_fastore = Opcode.opc_fastore; + + /** + * Opcode class for dastore. + */ + public static final int opcx_dastore = Opcode.opc_dastore; + + /** + * Opcode class for aastore. + */ + public static final int opcx_aastore = Opcode.opc_aastore; + + /** + * Opcode class for bastore. + */ + public static final int opcx_bastore = Opcode.opc_bastore; + + /** + * Opcode class for castore. + */ + public static final int opcx_castore = Opcode.opc_castore; + + /** + * Opcode class for sastore. + */ + public static final int opcx_sastore = Opcode.opc_sastore; + + /** + * Opcode class for pop. + */ + public static final int opcx_pop = Opcode.opc_pop; + + /** + * Opcode class for pop2. + */ + public static final int opcx_pop2 = Opcode.opc_pop2; + + /** + * Opcode class for dup. + */ + public static final int opcx_dup = Opcode.opc_dup; + + /** + * Opcode class for dup_x1. + */ + public static final int opcx_dup_x1 = Opcode.opc_dup_x1; + + /** + * Opcode class for dup_x2. + */ + public static final int opcx_dup_x2 = Opcode.opc_dup_x2; + + /** + * Opcode class for dup2. + */ + public static final int opcx_dup2 = Opcode.opc_dup2; + + /** + * Opcode class for dup2_x1. + */ + public static final int opcx_dup2_x1 = Opcode.opc_dup2_x1; + + /** + * Opcode class for dup2_x2. + */ + public static final int opcx_dup2_x2 = Opcode.opc_dup2_x2; + + /** + * Opcode class for swap. + */ + public static final int opcx_swap = Opcode.opc_swap; + + /** + * Opcode class for iadd. + */ + public static final int opcx_iadd = Opcode.opc_iadd; + + /** + * Opcode class for ladd. + */ + public static final int opcx_ladd = Opcode.opc_ladd; + + /** + * Opcode class for fadd. + */ + public static final int opcx_fadd = Opcode.opc_fadd; + + /** + * Opcode class for dadd. + */ + public static final int opcx_dadd = Opcode.opc_dadd; + + /** + * Opcode class for isub. + */ + public static final int opcx_isub = Opcode.opc_isub; + + /** + * Opcode class for lsub. + */ + public static final int opcx_lsub = Opcode.opc_lsub; + + /** + * Opcode class for fsub. + */ + public static final int opcx_fsub = Opcode.opc_fsub; + + /** + * Opcode class for dsub. + */ + public static final int opcx_dsub = Opcode.opc_dsub; + + /** + * Opcode class for imul. + */ + public static final int opcx_imul = Opcode.opc_imul; + + /** + * Opcode class for lmul. + */ + public static final int opcx_lmul = Opcode.opc_lmul; + + /** + * Opcode class for fmul. + */ + public static final int opcx_fmul = Opcode.opc_fmul; + + /** + * Opcode class for dmul. + */ + public static final int opcx_dmul = Opcode.opc_dmul; + + /** + * Opcode class for idiv. + */ + public static final int opcx_idiv = Opcode.opc_idiv; + + /** + * Opcode class for ldiv. + */ + public static final int opcx_ldiv = Opcode.opc_ldiv; + + /** + * Opcode class for fdiv. + */ + public static final int opcx_fdiv = Opcode.opc_fdiv; + + /** + * Opcode class for ddiv. + */ + public static final int opcx_ddiv = Opcode.opc_ddiv; + + /** + * Opcode class for irem. + */ + public static final int opcx_irem = Opcode.opc_irem; + + /** + * Opcode class for lrem. + */ + public static final int opcx_lrem = Opcode.opc_lrem; + + /** + * Opcode class for frem. + */ + public static final int opcx_frem = Opcode.opc_frem; + + /** + * Opcode class for drem. + */ + public static final int opcx_drem = Opcode.opc_drem; + + /** + * Opcode class for ineg. + */ + public static final int opcx_ineg = Opcode.opc_ineg; + + /** + * Opcode class for lneg. + */ + public static final int opcx_lneg = Opcode.opc_lneg; + + /** + * Opcode class for fneg. + */ + public static final int opcx_fneg = Opcode.opc_fneg; + + /** + * Opcode class for dneg. + */ + public static final int opcx_dneg = Opcode.opc_dneg; + + /** + * Opcode class for ishl. + */ + public static final int opcx_ishl = Opcode.opc_ishl; + + /** + * Opcode class for lshl. + */ + public static final int opcx_lshl = Opcode.opc_lshl; + + /** + * Opcode class for ishr. + */ + public static final int opcx_ishr = Opcode.opc_ishr; + + /** + * Opcode class for lshr. + */ + public static final int opcx_lshr = Opcode.opc_lshr; + + /** + * Opcode class for iushr. + */ + public static final int opcx_iushr = Opcode.opc_iushr; + + /** + * Opcode class for lushr. + */ + public static final int opcx_lushr = Opcode.opc_lushr; + + /** + * Opcode class for iand. + */ + public static final int opcx_iand = Opcode.opc_iand; + + /** + * Opcode class for land. + */ + public static final int opcx_land = Opcode.opc_land; + + /** + * Opcode class for ior. + */ + public static final int opcx_ior = Opcode.opc_ior; + + /** + * Opcode class for lor. + */ + public static final int opcx_lor = Opcode.opc_lor; + + /** + * Opcode class for ixor. + */ + public static final int opcx_ixor = Opcode.opc_ixor; + + /** + * Opcode class for lxor. + */ + public static final int opcx_lxor = Opcode.opc_lxor; + + /** + * Opcode class for iinc. + */ + public static final int opcx_iinc = Opcode.opc_iinc; + + /** + * Opcode class for i2l. + */ + public static final int opcx_i2l = Opcode.opc_i2l; + + /** + * Opcode class for i2f. + */ + public static final int opcx_i2f = Opcode.opc_i2f; + + /** + * Opcode class for i2d. + */ + public static final int opcx_i2d = Opcode.opc_i2d; + + /** + * Opcode class for l2i. + */ + public static final int opcx_l2i = Opcode.opc_l2i; + + /** + * Opcode class for l2f. + */ + public static final int opcx_l2f = Opcode.opc_l2f; + + /** + * Opcode class for l2d. + */ + public static final int opcx_l2d = Opcode.opc_l2d; + + /** + * Opcode class for f2i. + */ + public static final int opcx_f2i = Opcode.opc_f2i; + + /** + * Opcode class for f2l. + */ + public static final int opcx_f2l = Opcode.opc_f2l; + + /** + * Opcode class for f2d. + */ + public static final int opcx_f2d = Opcode.opc_f2d; + + /** + * Opcode class for d2i. + */ + public static final int opcx_d2i = Opcode.opc_d2i; + + /** + * Opcode class for d2l. + */ + public static final int opcx_d2l = Opcode.opc_d2l; + + /** + * Opcode class for d2f. + */ + public static final int opcx_d2f = Opcode.opc_d2f; + + /** + * Opcode class for i2b. + */ + public static final int opcx_i2b = Opcode.opc_i2b; + + /** + * Opcode class for i2c. + */ + public static final int opcx_i2c = Opcode.opc_i2c; + + /** + * Opcode class for i2s. + */ + public static final int opcx_i2s = Opcode.opc_i2s; + + /** + * Opcode class for lcmp. + */ + public static final int opcx_lcmp = Opcode.opc_lcmp; + + /** + * Opcode class for fcmpl. + */ + public static final int opcx_fcmpl = Opcode.opc_fcmpl; + + /** + * Opcode class for fcmpg. + */ + public static final int opcx_fcmpg = Opcode.opc_fcmpg; + + /** + * Opcode class for dcmpl. + */ + public static final int opcx_dcmpl = Opcode.opc_dcmpl; + + /** + * Opcode class for dcmpg. + */ + public static final int opcx_dcmpg = Opcode.opc_dcmpg; + + /** + * Opcode class for ifeq. + */ + public static final int opcx_ifeq = Opcode.opc_ifeq; + + /** + * Opcode class for ifne. + */ + public static final int opcx_ifne = Opcode.opc_ifne; + + /** + * Opcode class for iflt. + */ + public static final int opcx_iflt = Opcode.opc_iflt; + + /** + * Opcode class for ifge. + */ + public static final int opcx_ifge = Opcode.opc_ifge; + + /** + * Opcode class for ifgt. + */ + public static final int opcx_ifgt = Opcode.opc_ifgt; + + /** + * Opcode class for ifle. + */ + public static final int opcx_ifle = Opcode.opc_ifle; + + /** + * Opcode class for if_icmpeq. + */ + public static final int opcx_if_icmpeq = Opcode.opc_if_icmpeq; + + /** + * Opcode class for if_icmpne. + */ + public static final int opcx_if_icmpne = Opcode.opc_if_icmpne; + + /** + * Opcode class for if_icmplt. + */ + public static final int opcx_if_icmplt = Opcode.opc_if_icmplt; + + /** + * Opcode class for if_icmpge. + */ + public static final int opcx_if_icmpge = Opcode.opc_if_icmpge; + + /** + * Opcode class for if_icmpgt. + */ + public static final int opcx_if_icmpgt = Opcode.opc_if_icmpgt; + + /** + * Opcode class for if_icmple. + */ + public static final int opcx_if_icmple = Opcode.opc_if_icmple; + + /** + * Opcode class for if_acmpeq. + */ + public static final int opcx_if_acmpeq = Opcode.opc_if_acmpeq; + + /** + * Opcode class for if_acmpne. + */ + public static final int opcx_if_acmpne = Opcode.opc_if_acmpne; + + /** + * Opcode class for goto, goto_w. + */ + public static final int opcx_goto = Opcode.opc_goto; + + /** + * Opcode class for jsr, jsr_w. + */ + public static final int opcx_jsr = Opcode.opc_jsr; + + /** + * Opcode class for ret. + */ + public static final int opcx_ret = Opcode.opc_ret; + + /** + * Opcode class for tableswitch, lookupswitch. + */ + public static final int opcx_switch = Opcode.opc_tableswitch; + + /** + * Opcode class for ireturn. + */ + public static final int opcx_ireturn = Opcode.opc_ireturn; + + /** + * Opcode class for lreturn. + */ + public static final int opcx_lreturn = Opcode.opc_lreturn; + + /** + * Opcode class for freturn. + */ + public static final int opcx_freturn = Opcode.opc_freturn; + + /** + * Opcode class for dreturn. + */ + public static final int opcx_dreturn = Opcode.opc_dreturn; + + /** + * Opcode class for areturn. + */ + public static final int opcx_areturn = Opcode.opc_areturn; + + /** + * Opcode class for return. + */ + public static final int opcx_return = Opcode.opc_return; + + /** + * Opcode class for getstatic. + */ + public static final int opcx_getstatic = Opcode.opc_getstatic; + + /** + * Opcode class for putstatic. + */ + public static final int opcx_putstatic = Opcode.opc_putstatic; + + /** + * Opcode class for getfield. + */ + public static final int opcx_getfield = Opcode.opc_getfield; + + /** + * Opcode class for putfield. + */ + public static final int opcx_putfield = Opcode.opc_putfield; + + /** + * Opcode class for invokevirtual. + */ + public static final int opcx_invokevirtual = Opcode.opc_invokevirtual; + + /** + * Opcode class for invokespecial. + */ + public static final int opcx_invokespecial = Opcode.opc_invokespecial; + + /** + * Opcode class for invokestatic. + */ + public static final int opcx_invokestatic = Opcode.opc_invokestatic; + + /** + * Opcode class for invokeinterface. + */ + public static final int opcx_invokeinterface = Opcode.opc_invokeinterface; + + /** + * Opcode class for new. + */ + public static final int opcx_new = Opcode.opc_new; + + /** + * Opcode class for newarray, anewarray. + */ + public static final int opcx_newarray = Opcode.opc_newarray; + + /** + * Opcode class for arraylength. + */ + public static final int opcx_arraylength = Opcode.opc_arraylength; + + /** + * Opcode class for athrow. + */ + public static final int opcx_athrow = Opcode.opc_athrow; + + /** + * Opcode class for checkcast. + */ + public static final int opcx_checkcast = Opcode.opc_checkcast; + + /** + * Opcode class for instanceof. + */ + public static final int opcx_instanceof = Opcode.opc_instanceof; + + /** + * Opcode class for monitorenter. + */ + public static final int opcx_monitorenter = Opcode.opc_monitorenter; + + /** + * Opcode class for monitorexit. + */ + public static final int opcx_monitorexit = Opcode.opc_monitorexit; + + /** + * Opcode class for multianewarray. + */ + public static final int opcx_multianewarray = Opcode.opc_multianewarray; + + /** + * Opcode class for ifnull. + */ + public static final int opcx_ifnull = Opcode.opc_ifnull; + + /** + * Opcode class for ifnonnull. + */ + public static final int opcx_ifnonnull = Opcode.opc_ifnonnull; + + /** + * Opcode class for supdate. + */ + public static final int opcx_aupdate = Opcode.opc_aupdate; + + /** + * Opcode class for supdate. + */ + public static final int opcx_supdate = Opcode.opc_supdate; + + /** + * Opcode class for rc. + */ + public static final int opcx_rc = Opcode.opc_rc; + + /** + * Opcode class for aswizzle. + */ + public static final int opcx_aswizzle = Opcode.opc_aswizzle; + + /** + * Opcode class for aswrange. + */ + public static final int opcx_aswrange = Opcode.opc_aswrange; + + /** + * Opcode class for putfield_nowb. + */ + public static final int opcx_putfield_nowb = Opcode.opc_putfield_nowb; + + /** + * Opcode class for putstatic_nowb. + */ + public static final int opcx_putstatic_nowb = Opcode.opc_putstatic_nowb; + + /** + * An array of opcode names, indexed by the opcode. + */ + public static final String[] opcNames = { "nop", "aconst_null", + "iconst_m1", "iconst_0", "iconst_1", "iconst_2", "iconst_3", + "iconst_4", "iconst_5", "lconst_0", "lconst_1", "fconst_0", + "fconst_1", "fconst_2", "dconst_0", "dconst_1", "bipush", "sipush", + "ldc", "ldc_w", "ldc2_w", "iload", "lload", "fload", "dload", + "aload", "iload_0", "iload_1", "iload_2", "iload_3", "lload_0", + "lload_1", "lload_2", "lload_3", "fload_0", "fload_1", "fload_2", + "fload_3", "dload_0", "dload_1", "dload_2", "dload_3", "aload_0", + "aload_1", "aload_2", "aload_3", "iaload", "laload", "faload", + "daload", "aaload", "baload", "caload", "saload", "istore", + "lstore", "fstore", "dstore", "astore", "istore_0", "istore_1", + "istore_2", "istore_3", "lstore_0", "lstore_1", "lstore_2", + "lstore_3", "fstore_0", "fstore_1", "fstore_2", "fstore_3", + "dstore_0", "dstore_1", "dstore_2", "dstore_3", "astore_0", + "astore_1", "astore_2", "astore_3", "iastore", "lastore", + "fastore", "dastore", "aastore", "bastore", "castore", "sastore", + "pop", "pop2", "dup", "dup_x1", "dup_x2", "dup2", "dup2_x1", + "dup2_x2", "swap", "iadd", "ladd", "fadd", "dadd", "isub", "lsub", + "fsub", "dsub", "imul", "lmul", "fmul", "dmul", "idiv", "ldiv", + "fdiv", "ddiv", "irem", "lrem", "frem", "drem", "ineg", "lneg", + "fneg", "dneg", "ishl", "lshl", "ishr", "lshr", "iushr", "lushr", + "iand", "land", "ior", "lor", "ixor", "lxor", "iinc", "i2l", "i2f", + "i2d", "l2i", "l2f", "l2d", "f2i", "f2l", "f2d", "d2i", "d2l", + "d2f", "i2b", "i2c", "i2s", "lcmp", "fcmpl", "fcmpg", "dcmpl", + "dcmpg", "ifeq", "ifne", "iflt", "ifge", "ifgt", "ifle", + "if_icmpeq", "if_icmpne", "if_icmplt", "if_icmpge", "if_icmpgt", + "if_icmple", "if_acmpeq", "if_acmpne", "goto", "jsr", "ret", + "tableswitch", "lookupswitch", "ireturn", "lreturn", "freturn", + "dreturn", "areturn", "return", "getstatic", "putstatic", + "getfield", "putfield", "invokevirtual", "invokespecial", + "invokestatic", "invokeinterface", "xxxunusedxxx", "new", + "newarray", "anewarray", "arraylength", "athrow", "checkcast", + "instanceof", "monitorenter", "monitorexit", "wide", + "multianewarray", "ifnull", "ifnonnull", "goto_w", "jsr_w", + "breakpoint", "203", "putfield_nowb", "putstatic_nowb", "206", + "207", "208", "209", "210", "211", "212", "213", "214", "215", + "216", "217", "218", "219", "220", "221", "222", "223", "224", + "225", "226", "227", "228", "229", "230", "231", "232", "233", + "234", "235", "236", "rc", "aupdate", "supdate", "aswizzle", + "aswrange", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255", }; + + /** + * VARIABLE represent either variable instruction length or a variable + * effect on the operand stack, depending on the context. + */ + public static final byte VARIABLE = -1; + + /** + * An array of the bytecode instruction lengths, indexed by the opcode. + */ + public static final byte[] opcSize = { 1, // nop + 1, // aconst_null + 1, // iconst_m1 + 1, // iconst_0 + 1, // iconst_1 + 1, // iconst_2 + 1, // iconst_3 + 1, // iconst_4 + 1, // iconst_5 + 1, // lconst_0 + 1, // lconst_1 + 1, // fconst_0 + 1, // fconst_1 + 1, // fconst_2 + 1, // dconst_0 + 1, // dconst_1 + 2, // bipush + 3, // sipush + 2, // ldc + 3, // ldc_w + 3, // ldc2_w + 2, // iload + 2, // lload + 2, // fload + 2, // dload + 2, // aload + 1, // iload_0 + 1, // iload_1 + 1, // iload_2 + 1, // iload_3 + 1, // lload_0 + 1, // lload_1 + 1, // lload_2 + 1, // lload_3 + 1, // fload_0 + 1, // fload_1 + 1, // fload_2 + 1, // fload_3 + 1, // dload_0 + 1, // dload_1 + 1, // dload_2 + 1, // dload_3 + 1, // aload_0 + 1, // aload_1 + 1, // aload_2 + 1, // aload_3 + 1, // iaload + 1, // laload + 1, // faload + 1, // daload + 1, // aaload + 1, // baload + 1, // caload + 1, // saload + 2, // istore + 2, // lstore + 2, // fstore + 2, // dstore + 2, // astore + 1, // istore_0 + 1, // istore_1 + 1, // istore_2 + 1, // istore_3 + 1, // lstore_0 + 1, // lstore_1 + 1, // lstore_2 + 1, // lstore_3 + 1, // fstore_0 + 1, // fstore_1 + 1, // fstore_2 + 1, // fstore_3 + 1, // dstore_0 + 1, // dstore_1 + 1, // dstore_2 + 1, // dstore_3 + 1, // astore_0 + 1, // astore_1 + 1, // astore_2 + 1, // astore_3 + 1, // iastore + 1, // lastore + 1, // fastore + 1, // dastore + 1, // aastore + 1, // bastore + 1, // castore + 1, // sastore + 1, // pop + 1, // pop2 + 1, // dup + 1, // dup_x1 + 1, // dup_x2 + 1, // dup2 + 1, // dup2_x1 + 1, // dup2_x2 + 1, // swap + 1, // iadd + 1, // ladd + 1, // fadd + 1, // dadd + 1, // isub + 1, // lsub + 1, // fsub + 1, // dsub + 1, // imul + 1, // lmul + 1, // fmul + 1, // dmul + 1, // idiv + 1, // ldiv + 1, // fdiv + 1, // ddiv + 1, // irem + 1, // lrem + 1, // frem + 1, // drem + 1, // ineg + 1, // lneg + 1, // fneg + 1, // dneg + 1, // ishl + 1, // lshl + 1, // ishr + 1, // lshr + 1, // iushr + 1, // lushr + 1, // iand + 1, // land + 1, // ior + 1, // lor + 1, // ixor + 1, // lxor + 3, // iinc + 1, // i2l + 1, // i2f + 1, // i2d + 1, // l2i + 1, // l2f + 1, // l2d + 1, // f2i + 1, // f2l + 1, // f2d + 1, // d2i + 1, // d2l + 1, // d2f + 1, // i2b + 1, // i2c + 1, // i2s + 1, // lcmp + 1, // fcmpl + 1, // fcmpg + 1, // dcmpl + 1, // dcmpg + 3, // ifeq + 3, // ifne + 3, // iflt + 3, // ifge + 3, // ifgt + 3, // ifle + 3, // if_icmpeq + 3, // if_icmpne + 3, // if_icmplt + 3, // if_icmpge + 3, // if_icmpgt + 3, // if_icmple + 3, // if_acmpeq + 3, // if_acmpne + 3, // goto + 3, // jsr + 2, // ret + Opcode.VARIABLE, // tableswitch + Opcode.VARIABLE, // lookupswitch + 1, // ireturn + 1, // lreturn + 1, // freturn + 1, // dreturn + 1, // areturn + 1, // return + 3, // getstatic + 3, // putstatic + 3, // getfield + 3, // putfield + 3, // invokevirtual + 3, // invokespecial + 3, // invokestatic + 5, // invokeinterface + 1, // xxxunusedxxx + 3, // new + 2, // newarray + 3, // anewarray + 1, // arraylength + 1, // athrow + 3, // checkcast + 3, // instanceof + 1, // monitorenter + 1, // monitorexit + Opcode.VARIABLE, // wide + 4, // multianewarray + 3, // ifnull + 3, // ifnonnull + 5, // goto_w + 5, // jsr_w + 1, // breakpoint + 1, // 203 + 3, // putfield_nowb + 3, // putstatic_nowb + 1, // 206 + 1, // 207 + 1, // 208 + 1, // 209 + 1, // 210 + 1, // 211 + 1, // 212 + 1, // 213 + 1, // 214 + 1, // 215 + 1, // 216 + 1, // 217 + 1, // 218 + 1, // 219 + 1, // 220 + 1, // 221 + 1, // 222 + 1, // 223 + 1, // 224 + 1, // 225 + 1, // 226 + 1, // 227 + 1, // 228 + 1, // 229 + 1, // 230 + 1, // 231 + 1, // 232 + 1, // 233 + 1, // 234 + 1, // 235 + 1, // 236 + 2, // rc + 2, // aupdate + 2, // supdate + 1, // aswizzle + 1, // aswrange + 1, // 242 + 1, // 243 + 1, // 244 + 1, // 245 + 1, // 246 + 1, // 247 + 1, // 248 + 1, // 249 + 1, // 250 + 1, // 251 + 1, // 252 + 1, // 253 + 1, // 254 + 1, // 255 + }; + + /** + * An array, indexed by the opcode, mapping opcodes to opcode classes. + */ + public static final int[] opcXMap = { Opcode.opcx_nop, // nop + Opcode.opcx_ldc, // aconst_null + Opcode.opcx_ldc, // iconst_m1 + Opcode.opcx_ldc, // iconst_0 + Opcode.opcx_ldc, // iconst_1 + Opcode.opcx_ldc, // iconst_2 + Opcode.opcx_ldc, // iconst_3 + Opcode.opcx_ldc, // iconst_4 + Opcode.opcx_ldc, // iconst_5 + Opcode.opcx_ldc, // lconst_0 + Opcode.opcx_ldc, // lconst_1 + Opcode.opcx_ldc, // fconst_0 + Opcode.opcx_ldc, // fconst_1 + Opcode.opcx_ldc, // fconst_2 + Opcode.opcx_ldc, // dconst_0 + Opcode.opcx_ldc, // dconst_1 + Opcode.opcx_ldc, // bipush + Opcode.opcx_ldc, // sipush + Opcode.opcx_ldc, // ldc + Opcode.opcx_ldc, // ldc_w + Opcode.opcx_ldc, // ldc2_w + Opcode.opcx_iload, // iload + Opcode.opcx_lload, // lload + Opcode.opcx_fload, // fload + Opcode.opcx_dload, // dload + Opcode.opcx_aload, // aload + Opcode.opcx_iload, // iload_0 + Opcode.opcx_iload, // iload_1 + Opcode.opcx_iload, // iload_2 + Opcode.opcx_iload, // iload_3 + Opcode.opcx_lload, // lload_0 + Opcode.opcx_lload, // lload_1 + Opcode.opcx_lload, // lload_2 + Opcode.opcx_lload, // lload_3 + Opcode.opcx_fload, // fload_0 + Opcode.opcx_fload, // fload_1 + Opcode.opcx_fload, // fload_2 + Opcode.opcx_fload, // fload_3 + Opcode.opcx_dload, // dload_0 + Opcode.opcx_dload, // dload_1 + Opcode.opcx_dload, // dload_2 + Opcode.opcx_dload, // dload_3 + Opcode.opcx_aload, // aload_0 + Opcode.opcx_aload, // aload_1 + Opcode.opcx_aload, // aload_2 + Opcode.opcx_aload, // aload_3 + Opcode.opcx_iaload, // iaload + Opcode.opcx_laload, // laload + Opcode.opcx_faload, // faload + Opcode.opcx_daload, // daload + Opcode.opcx_aaload, // aaload + Opcode.opcx_baload, // baload + Opcode.opcx_caload, // caload + Opcode.opcx_saload, // saload + Opcode.opcx_istore, // istore + Opcode.opcx_lstore, // lstore + Opcode.opcx_fstore, // fstore + Opcode.opcx_dstore, // dstore + Opcode.opcx_astore, // astore + Opcode.opcx_istore, // istore_0 + Opcode.opcx_istore, // istore_1 + Opcode.opcx_istore, // istore_2 + Opcode.opcx_istore, // istore_3 + Opcode.opcx_lstore, // lstore_0 + Opcode.opcx_lstore, // lstore_1 + Opcode.opcx_lstore, // lstore_2 + Opcode.opcx_lstore, // lstore_3 + Opcode.opcx_fstore, // fstore_0 + Opcode.opcx_fstore, // fstore_1 + Opcode.opcx_fstore, // fstore_2 + Opcode.opcx_fstore, // fstore_3 + Opcode.opcx_dstore, // dstore_0 + Opcode.opcx_dstore, // dstore_1 + Opcode.opcx_dstore, // dstore_2 + Opcode.opcx_dstore, // dstore_3 + Opcode.opcx_astore, // astore_0 + Opcode.opcx_astore, // astore_1 + Opcode.opcx_astore, // astore_2 + Opcode.opcx_astore, // astore_3 + Opcode.opcx_iastore, // iastore + Opcode.opcx_lastore, // lastore + Opcode.opcx_fastore, // fastore + Opcode.opcx_dastore, // dastore + Opcode.opcx_aastore, // aastore + Opcode.opcx_bastore, // bastore + Opcode.opcx_castore, // castore + Opcode.opcx_sastore, // sastore + Opcode.opcx_pop, // pop + Opcode.opcx_pop2, // pop2 + Opcode.opcx_dup, // dup + Opcode.opcx_dup_x1, // dup_x1 + Opcode.opcx_dup_x2, // dup_x2 + Opcode.opcx_dup2, // dup2 + Opcode.opcx_dup2_x1, // dup2_x1 + Opcode.opcx_dup2_x2, // dup2_x2 + Opcode.opcx_swap, // swap + Opcode.opcx_iadd, // iadd + Opcode.opcx_ladd, // ladd + Opcode.opcx_fadd, // fadd + Opcode.opcx_dadd, // dadd + Opcode.opcx_isub, // isub + Opcode.opcx_lsub, // lsub + Opcode.opcx_fsub, // fsub + Opcode.opcx_dsub, // dsub + Opcode.opcx_imul, // imul + Opcode.opcx_lmul, // lmul + Opcode.opcx_fmul, // fmul + Opcode.opcx_dmul, // dmul + Opcode.opcx_idiv, // idiv + Opcode.opcx_ldiv, // ldiv + Opcode.opcx_fdiv, // fdiv + Opcode.opcx_ddiv, // ddiv + Opcode.opcx_irem, // irem + Opcode.opcx_lrem, // lrem + Opcode.opcx_frem, // frem + Opcode.opcx_drem, // drem + Opcode.opcx_ineg, // ineg + Opcode.opcx_lneg, // lneg + Opcode.opcx_fneg, // fneg + Opcode.opcx_dneg, // dneg + Opcode.opcx_ishl, // ishl + Opcode.opcx_lshl, // lshl + Opcode.opcx_ishr, // ishr + Opcode.opcx_lshr, // lshr + Opcode.opcx_iushr, // iushr + Opcode.opcx_lushr, // lushr + Opcode.opcx_iand, // iand + Opcode.opcx_land, // land + Opcode.opcx_ior, // ior + Opcode.opcx_lor, // lor + Opcode.opcx_ixor, // ixor + Opcode.opcx_lxor, // lxor + Opcode.opcx_iinc, // iinc + Opcode.opcx_i2l, // i2l + Opcode.opcx_i2f, // i2f + Opcode.opcx_i2d, // i2d + Opcode.opcx_l2i, // l2i + Opcode.opcx_l2f, // l2f + Opcode.opcx_l2d, // l2d + Opcode.opcx_f2i, // f2i + Opcode.opcx_f2l, // f2l + Opcode.opcx_f2d, // f2d + Opcode.opcx_d2i, // d2i + Opcode.opcx_d2l, // d2l + Opcode.opcx_d2f, // d2f + Opcode.opcx_i2b, // i2b + Opcode.opcx_i2c, // i2c + Opcode.opcx_i2s, // i2s + Opcode.opcx_lcmp, // lcmp + Opcode.opcx_fcmpl, // fcmpl + Opcode.opcx_fcmpg, // fcmpg + Opcode.opcx_dcmpl, // dcmpl + Opcode.opcx_dcmpg, // dcmpg + Opcode.opcx_ifeq, // ifeq + Opcode.opcx_ifne, // ifne + Opcode.opcx_iflt, // iflt + Opcode.opcx_ifge, // ifge + Opcode.opcx_ifgt, // ifgt + Opcode.opcx_ifle, // ifle + Opcode.opcx_if_icmpeq, // if_icmpeq + Opcode.opcx_if_icmpne, // if_icmpne + Opcode.opcx_if_icmplt, // if_icmplt + Opcode.opcx_if_icmpge, // if_icmpge + Opcode.opcx_if_icmpgt, // if_icmpgt + Opcode.opcx_if_icmple, // if_icmple + Opcode.opcx_if_acmpeq, // if_acmpeq + Opcode.opcx_if_acmpne, // if_acmpne + Opcode.opcx_goto, // goto + Opcode.opcx_jsr, // jsr + Opcode.opcx_ret, // ret + Opcode.opcx_switch, // tableswitch + Opcode.opcx_switch, // lookupswitch + Opcode.opcx_ireturn, // ireturn + Opcode.opcx_lreturn, // lreturn + Opcode.opcx_freturn, // freturn + Opcode.opcx_dreturn, // dreturn + Opcode.opcx_areturn, // areturn + Opcode.opcx_return, // return + Opcode.opcx_getstatic, // getstatic + Opcode.opcx_putstatic, // putstatic + Opcode.opcx_getfield, // getfield + Opcode.opcx_putfield, // putfield + Opcode.opcx_invokevirtual, // invokevirtual + Opcode.opcx_invokespecial, // invokespecial + Opcode.opcx_invokestatic, // invokestatic + Opcode.opcx_invokeinterface, // invokeinterface + Opcode.opcx_nop, // xxxunusedxxx + Opcode.opcx_new, // new + Opcode.opcx_newarray, // newarray + Opcode.opcx_newarray, // anewarray + Opcode.opcx_arraylength, // arraylength + Opcode.opcx_athrow, // athrow + Opcode.opcx_checkcast, // checkcast + Opcode.opcx_instanceof, // instanceof + Opcode.opcx_monitorenter, // monitorenter + Opcode.opcx_monitorexit, // monitorexit + Opcode.opcx_nop, // wide + Opcode.opcx_multianewarray, // multianewarray + Opcode.opcx_ifnull, // ifnull + Opcode.opcx_ifnonnull, // ifnonnull + Opcode.opcx_goto, // goto_w + Opcode.opcx_jsr, // jsr_w + Opcode.opcx_nop, // breakpoint + Opcode.opcx_nop, // 203 + Opcode.opcx_putfield_nowb, // putfield_nowb + Opcode.opcx_putstatic_nowb, // putstatic_nowb + Opcode.opcx_nop, // 206 + Opcode.opcx_nop, // 207 + Opcode.opcx_nop, // 208 + Opcode.opcx_nop, // 209 + Opcode.opcx_nop, // 210 + Opcode.opcx_nop, // 211 + Opcode.opcx_nop, // 212 + Opcode.opcx_nop, // 213 + Opcode.opcx_nop, // 214 + Opcode.opcx_nop, // 215 + Opcode.opcx_nop, // 216 + Opcode.opcx_nop, // 217 + Opcode.opcx_nop, // 218 + Opcode.opcx_nop, // 219 + Opcode.opcx_nop, // 220 + Opcode.opcx_nop, // 221 + Opcode.opcx_nop, // 222 + Opcode.opcx_nop, // 223 + Opcode.opcx_nop, // 224 + Opcode.opcx_nop, // 225 + Opcode.opcx_nop, // 226 + Opcode.opcx_nop, // 227 + Opcode.opcx_nop, // 228 + Opcode.opcx_nop, // 229 + Opcode.opcx_nop, // 230 + Opcode.opcx_nop, // 231 + Opcode.opcx_nop, // 232 + Opcode.opcx_nop, // 233 + Opcode.opcx_nop, // 234 + Opcode.opcx_nop, // 235 + Opcode.opcx_nop, // 236 + Opcode.opcx_rc, // rc + Opcode.opcx_aupdate, // aupdate + Opcode.opcx_supdate, // supdate + Opcode.opcx_aswizzle, // aswizzle + Opcode.opcx_aswrange, // aswrange + Opcode.opcx_nop, // 242 + Opcode.opcx_nop, // 243 + Opcode.opcx_nop, // 244 + Opcode.opcx_nop, // 245 + Opcode.opcx_nop, // 246 + Opcode.opcx_nop, // 247 + Opcode.opcx_nop, // 248 + Opcode.opcx_nop, // 249 + Opcode.opcx_nop, // 250 + Opcode.opcx_nop, // 251 + Opcode.opcx_nop, // 252 + Opcode.opcx_nop, // 253 + Opcode.opcx_nop, // 254 + Opcode.opcx_nop, // 255 + }; +} diff --git a/src/EDU/purdue/cs/bloat/editor/SerialVersionUID.java b/src/EDU/purdue/cs/bloat/editor/SerialVersionUID.java new file mode 100644 index 0000000..3158094 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/SerialVersionUID.java @@ -0,0 +1,459 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.io.*; +import java.lang.reflect.*; +import java.security.*; +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + *

+ * This class computes the serial version UID of a class modeled by a + * ClassEditor. Otherwise, we would have to load the class in + * order to compute its serial version UID. That would suck. + *

+ * + *

+ * The algorithm for computing the serial version UID can be found in the serialization + * spec + *

+ */ +public class SerialVersionUID { + + /** + * Returns true if the class modeled by the given + * ClassEditor implements {@link java.io.Serializable + * Serializable}. It checks superclasses. + */ + public static boolean implementsSerializable(final ClassEditor ce) { + if (ce.type().equals(Type.OBJECT)) { + // Stop the recursion! + return (false); + } + + final Type serializable = Type.getType("Ljava/io/Serializable;"); + final Type[] interfaces = ce.interfaces(); + for (int i = 0; i < interfaces.length; i++) { + if (interfaces[i].equals(serializable)) { + return (true); + } + } + + // Does its superclass implement Serializable? + + final Type superclass = ce.superclass(); + final ClassInfoLoader loader = ce.classInfo().loader(); + try { + final ClassInfo ci = loader.loadClass(superclass.className()); + final ClassEditor sce = new ClassEditor(ce.context(), ci); + return (SerialVersionUID.implementsSerializable(sce)); + + } catch (final ClassNotFoundException ex) { + System.err.println("Could not load class: " + superclass + + ", superclass of " + ce.name()); + System.exit(1); + } + return (false); + } + + /** + * Returns the serial version UID of the class modeled by the given + * ClassEditor. + * + * @param ce + * The class must implement {@link java.io.Serializable + * Serializable} + */ + public static long serialVersionUID(final ClassEditor ce) { + // Make sure the class implements Serializable + if (!SerialVersionUID.implementsSerializable(ce)) { + final String s = "Class " + ce.name() + + " does not implement java.io.Serializable"; + throw new IllegalArgumentException(s); + } + + // If the class already has a serialVersionUID, return that + final FieldInfo[] fields = ce.fields(); + for (int i = 0; i < fields.length; i++) { + final FieldEditor fe = new FieldEditor(ce, fields[i]); + if (fe.name().equals("serialVersionUID")) { + final Object value = fe.constantValue(); + if (value != null) { + if (value instanceof Long) { + return (((Long) value).longValue()); + } + } + } + } + + // Now, compute the digest of the bytes using SHA + MessageDigest algorithm = null; + try { + algorithm = MessageDigest.getInstance("SHA"); + + } catch (final NoSuchAlgorithmException ex) { + final String s = "Can't use SHA-1 message digest algorith!"; + throw new IllegalArgumentException(s); + } + + final DataOutputStream dos = new DataOutputStream( + new DigestOutputStream(new ByteArrayOutputStream(), algorithm)); + + try { + // Write a bunch of information about the class to the output + // stream + SerialVersionUID.writeClassName(ce, dos); + SerialVersionUID.writeClassModifiers(ce, dos); + SerialVersionUID.writeInterfaceNames(ce, dos); + SerialVersionUID.writeFields(ce, dos); + SerialVersionUID.writeStaticInitializer(ce, dos); + SerialVersionUID.writeConstructors(ce, dos); + SerialVersionUID.writeMethods(ce, dos); + + dos.flush(); + dos.close(); + + } catch (final IOException ex) { + final String s = ("While computing serial version UID: " + ex); + throw new IllegalArgumentException(s); + } + + // Compute the hash value from the first 64 bites of the digest + final byte[] digest = algorithm.digest(); + long uid = 0; + for (int i = 0; i < Math.min(8, digest.length); i++) { + uid += (long) (digest[i] & 255) << (i * 8); + } + return (uid); + + } + + /** + * Writes the name of the class to the data output stream + */ + private static void writeClassName(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + dos.writeUTF(ce.name().replace('/', '.')); + } + + /** + * Returns the Java reflection modifiers for a given class + */ + static int getModifiers(final ClassEditor ce) { + // Translate BLOAT's class modifiers into Java's reflection + // modifiers + int modifiers = 0; + + if (ce.isPublic()) { + modifiers |= Modifier.PUBLIC; + } + + if (ce.isPrivate()) { + modifiers |= Modifier.PRIVATE; + } + + if (ce.isProtected()) { + modifiers |= Modifier.PROTECTED; + } + + if (ce.isStatic()) { + modifiers |= Modifier.STATIC; + } + + if (ce.isFinal()) { + modifiers |= Modifier.FINAL; + } + + if (ce.isAbstract()) { + modifiers |= Modifier.ABSTRACT; + } + + if (ce.isInterface()) { + modifiers |= Modifier.INTERFACE; + } + + return (modifiers); + } + + /** + * Writes the class's modifiers to the output stream + */ + private static void writeClassModifiers(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + dos.writeInt(SerialVersionUID.getModifiers(ce)); + } + + /** + * Writes the names of the interfaces implemented by the class to the output + * stream + */ + private static void writeInterfaceNames(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + + // Sort interfaces by name + final SortedSet sorted = new TreeSet(); + + final Type[] interfaces = ce.interfaces(); + for (int i = 0; i < interfaces.length; i++) { + sorted.add(interfaces[i].className().replace('/', '.')); + } + + final Iterator iter = sorted.iterator(); + while (iter.hasNext()) { + final String name = (String) iter.next(); + dos.writeUTF(name); + } + + } + + /** + * Returns the Java reflection modifiers for a field + */ + static int getModifiers(final FieldEditor fe) { + int modifiers = 0; + + if (fe.isPublic()) { + modifiers |= Modifier.PUBLIC; + } + + if (fe.isPrivate()) { + modifiers |= Modifier.PRIVATE; + } + + if (fe.isProtected()) { + modifiers |= Modifier.PROTECTED; + } + + if (fe.isPackage()) { + // Nothing + } + + if (fe.isStatic()) { + modifiers |= Modifier.STATIC; + } + + if (fe.isFinal()) { + modifiers |= Modifier.FINAL; + } + + if (fe.isVolatile()) { + modifiers |= Modifier.VOLATILE; + } + + if (fe.isTransient()) { + // Kind of a moot point + modifiers |= Modifier.TRANSIENT; + } + + return (modifiers); + } + + /** + * Writes information about the class's fields to the output stream + */ + private static void writeFields(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + + // Sort the fields by their names + final SortedSet sorted = new TreeSet(new Comparator() { + public int compare(Object o1, Object o2) { + FieldEditor fe1 = (FieldEditor) o1; + FieldEditor fe2 = (FieldEditor) o2; + return (fe1.name().compareTo(fe2.name())); + } + }); + + final FieldInfo[] infos = ce.fields(); + for (int i = 0; i < infos.length; i++) { + final FieldEditor fe = new FieldEditor(ce, infos[i]); + // Ignore private static and private transient fields + if (fe.isPrivate() && fe.isStatic()) { + break; + + } else if (fe.isPrivate() && fe.isTransient()) { + break; + + } else { + sorted.add(fe); + } + } + + final Iterator iter = sorted.iterator(); + while (iter.hasNext()) { + final FieldEditor fe = (FieldEditor) iter.next(); + dos.writeUTF(fe.name()); + dos.writeInt(SerialVersionUID.getModifiers(fe)); + dos.writeUTF(fe.type().descriptor()); + } + } + + /** + * Returns the Java reflection descriptors for a method + */ + static int getModifiers(final MethodEditor me) { + int modifiers = 0; + + if (me.isPublic()) { + modifiers |= Modifier.PUBLIC; + } + + if (me.isPrivate()) { + modifiers |= Modifier.PRIVATE; + } + + if (me.isProtected()) { + modifiers |= Modifier.PROTECTED; + } + + if (me.isPackage()) { + // Nothing + } + + if (me.isStatic()) { + modifiers |= Modifier.STATIC; + } + + if (me.isFinal()) { + modifiers |= Modifier.FINAL; + } + + if (me.isSynchronized()) { + modifiers |= Modifier.SYNCHRONIZED; + } + + if (me.isNative()) { + modifiers |= Modifier.NATIVE; + } + + if (me.isAbstract()) { + modifiers |= Modifier.ABSTRACT; + } + + if (me.isInterface()) { + modifiers |= Modifier.INTERFACE; + } + + return (modifiers); + } + + /** + * Writes information about the classes static initializer if it has one + */ + private static void writeStaticInitializer(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + + MethodEditor clinit = null; + + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = new MethodEditor(ce, methods[i]); + if (me.name().equals("")) { + clinit = me; + break; + } + } + + if (clinit != null) { + dos.writeUTF(""); + dos.writeInt(Modifier.STATIC); + dos.writeUTF("()V"); + } + } + + /** + * Writes information about the class's constructors + */ + private static void writeConstructors(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + + // Sort constructors by their signatures + final SortedSet sorted = new TreeSet(new Comparator() { + public int compare(Object o1, Object o2) { + MethodEditor me1 = (MethodEditor) o1; + MethodEditor me2 = (MethodEditor) o2; + return (me1.type().descriptor().compareTo(me2.type() + .descriptor())); + } + }); + + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = new MethodEditor(ce, methods[i]); + if (me.name().equals("")) { + if (!me.isPrivate()) { + // Ignore private constructors + sorted.add(me); + } + } + } + + final Iterator iter = sorted.iterator(); + while (iter.hasNext()) { + final MethodEditor init = (MethodEditor) iter.next(); + dos.writeUTF(""); + dos.writeInt(SerialVersionUID.getModifiers(init)); + dos.writeUTF(init.type().descriptor()); + } + } + + /** + * Write information about the class's methods + */ + private static void writeMethods(final ClassEditor ce, + final DataOutputStream dos) throws IOException { + + // Sort constructors by their names and signatures + final SortedSet sorted = new TreeSet(new Comparator() { + public int compare(Object o1, Object o2) { + MethodEditor me1 = (MethodEditor) o1; + MethodEditor me2 = (MethodEditor) o2; + + String d1 = me1.name() + me1.type().descriptor(); + String d2 = me2.name() + me2.type().descriptor(); + return (d1.compareTo(d2)); + } + }); + + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = new MethodEditor(ce, methods[i]); + if (!me.isPrivate() && !me.isConstructor() + && !me.name().equals("")) { + // Ignore private methods + sorted.add(me); + } + } + + final Iterator iter = sorted.iterator(); + while (iter.hasNext()) { + final MethodEditor me = (MethodEditor) iter.next(); + dos.writeUTF(me.name()); + dos.writeInt(SerialVersionUID.getModifiers(me)); + dos.writeUTF(me.type().descriptor()); + } + + } + +} diff --git a/src/EDU/purdue/cs/bloat/editor/Switch.java b/src/EDU/purdue/cs/bloat/editor/Switch.java new file mode 100644 index 0000000..f7670aa --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Switch.java @@ -0,0 +1,232 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * Switch is used to hold the lookup values and branch targets of a tableswitch + * or lookup switch instruction. + *

+ * The tableswitch low-to-high range of values is represented by storing each + * lookup value in the range. This allows the tableswitch to be replaced with a + * lookupswitch if branches are deleted. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Switch { + private Label defaultTarget; + + private Label[] targets; + + private int[] values; + + /** + * Constructor. + * + * @param defaultTarget + * The default target of the switch. + * @param targets + * The non-default branch targets of the switch. + * @param values + * The lookup values of the switch. This array must be the same + * size as the targets array. + */ + public Switch(final Label defaultTarget, final Label[] targets, + final int[] values) { + this.defaultTarget = defaultTarget; + this.targets = targets; + this.values = values; + sort(); + uniq(); + } + + /** + * Set the default target of the switch. + * + * @param target + * The default target of the switch. + */ + public void setDefaultTarget(final Label target) { + defaultTarget = target; + } + + /** + * Get the default target of the switch. + * + * @return The default target of the switch. + */ + public Label defaultTarget() { + return defaultTarget; + } + + /** + * Get the non-default branch targets of the switch. The targets are sorted + * by the corresponding lookup value. + * + * @return The non-default branch targets of the switch. + */ + public Label[] targets() { + return targets; + } + + /** + * Get the lookup values of the switch, sorted low to high. + * + * @return The lookup values of the switch. + */ + public int[] values() { + return values; + } + + /** + * Check if the all the values in the range of lookup values are contiguous. + * If they are, a table switch can be used. If not a lookupswitch can be + * used. + * + * @return true if contiguous, false if not. + */ + public boolean hasContiguousValues() { + return values.length == highValue() - lowValue() + 1; + } + + /** + * Get the low value in the range the lookup values. + * + * @return The low value. + */ + public int lowValue() { + return values[0]; + } + + /** + * Get the high value in the range the lookup values. + * + * @return The high value. + */ + public int highValue() { + return values[values.length - 1]; + } + + /** + * Sort the targets and values arrays so that values is sorted low to high. + */ + private void sort() { + quicksort(0, values.length - 1); + } + + /** + * Utility function to sort the targets and values arrays so that values is + * sorted low to high. + * + * @param p + * The low index of the portion of the array to sort. + * @param r + * The high index of the portion of the array to sort. + */ + private void quicksort(final int p, final int r) { + if (p < r) { + final int q = partition(p, r); + quicksort(p, q); + quicksort(q + 1, r); + } + } + + /** + * Utility function to sort the targets and values arrays so that values is + * sorted low to high. + *

+ * Partition the arrays so that the values less than values[p] are to the + * left. + * + * @param p + * The low index of the portion of the array to sort. + * @param r + * The high index of the portion of the array to sort. + * @return The index at which the partition finished. + */ + private int partition(final int p, final int r) { + final int x = values[p]; + int i = p - 1; + int j = r + 1; + + while (true) { + do { + j--; + } while (values[j] > x); + + do { + i++; + } while (values[i] < x); + + if (i < j) { + final int v = values[i]; + values[i] = values[j]; + values[j] = v; + + final Label t = targets[i]; + targets[i] = targets[j]; + targets[j] = t; + } else { + return j; + } + } + } + + /** + * Remove duplicates from the values and targets arrays. + */ + private void uniq() { + if (values.length == 0) { + return; + } + + final int[] v = new int[values.length]; + final Label[] t = new Label[values.length]; + + v[0] = values[0]; + t[0] = targets[0]; + + int j = 1; + + for (int i = 1; i < values.length; i++) { + if (v[j - 1] != values[i]) { + v[j] = values[i]; + t[j] = targets[i]; + j++; + } + } + + values = new int[j]; + System.arraycopy(v, 0, values, 0, j); + + targets = new Label[j]; + System.arraycopy(t, 0, targets, 0, j); + } + + /** + * Convert the operand to a string. + * + * @return A string representation of the operand. + */ + public String toString() { + return "" + values.length + " pairs"; + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/TryCatch.java b/src/EDU/purdue/cs/bloat/editor/TryCatch.java new file mode 100644 index 0000000..d6cef7d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/TryCatch.java @@ -0,0 +1,106 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +/** + * TryCatch holds the labels for the start and end of a protected block and the + * beginning of a catch block and the type of the exception to catch. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class TryCatch { + private Label start; + + private Label end; + + private Label handler; + + private Type type; + + /** + * Constructor. + * + * @param start + * The start label of the protected block. + * @param end + * The label of the instruction after the end of the protected + * block. + * @param handler + * The start label of the exception handler. + * @param type + * The type of exception to catch. + */ + public TryCatch(final Label start, final Label end, final Label handler, + final Type type) { + this.start = start; + this.end = end; + this.handler = handler; + this.type = type; + } + + /** + * Get the start label of the protected block. + * + * @return The start label. + */ + public Label start() { + return start; + } + + /** + * Get the end label of the protected block. + * + * @return The end label. + */ + public Label end() { + return end; + } + + /** + * Get the start label of the catch block. + * + * @return The handler label. + */ + public Label handler() { + return handler; + } + + /** + * Set the start label of the catch block. + */ + public void setHandler(final Label handler) { + this.handler = handler; + } + + /** + * Get the type of the exception to catch. + * + * @return The type of the exception to catch. + */ + public Type type() { + return type; + } + + public String toString() { + return "try " + start + ".." + end + " catch (" + type + ") " + handler; + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/Type.java b/src/EDU/purdue/cs/bloat/editor/Type.java new file mode 100644 index 0000000..f574b86 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/Type.java @@ -0,0 +1,1035 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.util.*; + +/** + * Type represents a type descriptor in a classes constant pool. A type + * descriptor describes the type of a field of method using a funky encoding + * which is described in the Java Virtual Machine Specification. For example, + * the field int x[] has the type descriptor: + *

+ *

[I
+ *

+ * The method String f(int a, boolean b, Object c) has the type + * descriptor: + *

+ *

(IZLjava/lang/Object;)Ljava/lang/String;
+ *

+ * + * @see EDU.purdue.cs.bloat.reflect.Constant Constant + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Type { + public static final char ARRAY_CHAR = '['; + + public static final char BOOLEAN_CHAR = 'Z'; + + public static final char BYTE_CHAR = 'B'; + + public static final char CHARACTER_CHAR = 'C'; + + public static final char CLASS_CHAR = 'L'; + + public static final char DOUBLE_CHAR = 'D'; + + public static final char FLOAT_CHAR = 'F'; + + public static final char INTEGER_CHAR = 'I'; + + public static final char LONG_CHAR = 'J'; + + public static final char SHORT_CHAR = 'S'; + + public static final char VOID_CHAR = 'V'; + + public static final char ADDRESS_CHAR = 'A'; // return address + + public static final int BOOLEAN_CODE = 4; + + public static final int CHARACTER_CODE = 5; + + public static final int FLOAT_CODE = 6; + + public static final int DOUBLE_CODE = 7; + + public static final int BYTE_CODE = 8; + + public static final int SHORT_CODE = 9; + + public static final int INTEGER_CODE = 10; + + public static final int LONG_CODE = 11; + + public static final Type OBJECT; + + public static final Type STRING; + + public static final Type CLASS; + + public static final Type THROWABLE; + + public static final Type CLONEABLE; + + public static final Type SERIALIZABLE; + + public static final Type NULL; + + public static final Type BOOLEAN; + + public static final Type CHARACTER; + + public static final Type FLOAT; + + public static final Type DOUBLE; + + public static final Type BYTE; + + public static final Type SHORT; + + public static final Type INTEGER; + + public static final Type LONG; + + public static final Type VOID; + + public static final Type ADDRESS; + + /** + * Print truncated (abbreviated) type names. + */ + public static boolean PRINT_TRUNCATED = false; + + private static Map types; // All types in existence + + static { + Type.types = new TreeMap(); // log(n) get + + OBJECT = Type.getType("Ljava/lang/Object;"); + STRING = Type.getType("Ljava/lang/String;"); + CLASS = Type.getType("Ljava/lang/Class;"); + THROWABLE = Type.getType("Ljava/lang/Throwable;"); + CLONEABLE = Type.getType("Ljava/lang/Cloneable;"); + SERIALIZABLE = Type.getType("Ljava/lang/Serializable;"); + NULL = Type.getType("Lnull!;"); + BOOLEAN = Type.getType(Type.BOOLEAN_CHAR); + CHARACTER = Type.getType(Type.CHARACTER_CHAR); + FLOAT = Type.getType(Type.FLOAT_CHAR); + DOUBLE = Type.getType(Type.DOUBLE_CHAR); + BYTE = Type.getType(Type.BYTE_CHAR); + SHORT = Type.getType(Type.SHORT_CHAR); + INTEGER = Type.getType(Type.INTEGER_CHAR); + LONG = Type.getType(Type.LONG_CHAR); + VOID = Type.getType(Type.VOID_CHAR); + ADDRESS = Type.getType(Type.ADDRESS_CHAR); + } + + String desc; // The descriptor of a Type. + + // A descriptor is a String representation of a field or method. + // Descriptors of basics types consist of a single character code + // such as B (BYTE_CHAR) for byte and D (DOUBLE_CHAR) for double. + // An object type is represented by L; Array + // descriptors have a [ for each dimension followed by the character + // describing the type (e.g. [[[D for double d[][][]). + // Method descriptors consist of the descriptors of the method's + // parameters delimited by parentheses followed by the descript for + // the method's return type. For example, the descriptor for: + // Object m(int, double, Thread) + // is: + // (IDLjava/lang/Thread;)Ljava/lang/Object; + + // If this is a type descriptor for a method, these are important + Type[] paramTypes; // Desriptors of parameters to a method + + Type returnType; // Descriptor of the return type of a method + + /** + * Returns a Type of a given descriptor. Equals descriptors will + * result in the same Type. + */ + public static Type getType(final String desc) { + // It might be the case that we're passed a string already in the + // form of a type descriptor. Remove any trailing ';' and leading + // 'L' + // if(desc.endsWith(";") && desc.startsWith("L")) { + // desc = desc.substring(1, desc.length() - 1); + // } + + Type type = (Type) Type.types.get(desc); + if (type == null) { + type = new Type(desc); + Type.types.put(desc, type); + } + return (type); + } + + /** + * Returns a Type that represents a given Class. + */ + public static Type getType(final Class c) { + if (c.equals(Boolean.TYPE)) { + return (Type.BOOLEAN); + + } else if (c.equals(Character.TYPE)) { + return (Type.CHARACTER); + + } else if (c.equals(Float.TYPE)) { + return (Type.FLOAT); + + } else if (c.equals(Double.TYPE)) { + return (Type.DOUBLE); + + } else if (c.equals(Byte.TYPE)) { + return (Type.BYTE); + + } else if (c.equals(Short.TYPE)) { + return (Type.SHORT); + + } else if (c.equals(Integer.TYPE)) { + return (Type.INTEGER); + + } else if (c.equals(Long.TYPE)) { + return (Type.LONG); + + } else if (c.equals(Void.TYPE)) { + return (Type.VOID); + } + + // (pr) + String name = c.getName().replace('.', '/'); + if (!c.isArray()) { + name = "L" + name + ";"; + } + return (Type.getType(name)); + } + + /** + * Returns the Type for a method with the given parameter and return types + */ + public static Type getType(final Type[] paramTypes, final Type returnType) { + final StringBuffer sb = new StringBuffer("("); + for (int i = 0; i < paramTypes.length; i++) { + sb.append(paramTypes[i].descriptor()); + } + sb.append(")"); + sb.append(returnType.descriptor()); + return (Type.getType(sb.toString())); + } + + /** + * Constructor. Creates a type descriptor from a String. + */ + private Type(final String desc) { + final ArrayList types = new ArrayList(); + String t = ""; + boolean method = false; + + this.desc = desc.intern(); + + int state = 0; + int i = 0; + + // Basically, we're parsing the desc String to get information about + // the descriptor. For instance, method descriptors begin with '('. + + if (desc.charAt(i) == '(') { + method = true; + i++; + } + + for (; i < desc.length(); i++) { + switch (state) { + case 0: + // Parameter descriptor + switch (desc.charAt(i)) { + case BYTE_CHAR: + case CHARACTER_CHAR: + case DOUBLE_CHAR: + case FLOAT_CHAR: + case INTEGER_CHAR: + case LONG_CHAR: + case SHORT_CHAR: + case VOID_CHAR: + case BOOLEAN_CHAR: + types.add(t + desc.charAt(i)); + t = ""; + break; + case CLASS_CHAR: + t += desc.charAt(i); + state = 1; + break; + case ARRAY_CHAR: + t += desc.charAt(i); + break; + case ')': + if (!method) { + final String s = "Invalid char in type descriptor: )\n" + + "Descriptor was: " + desc; + throw new IllegalArgumentException(s); + } + break; + default: + throw new IllegalArgumentException("Invalid char in " + + "type descriptor (" + desc + "): " + + desc.charAt(i)); + } + break; + + case 1: // Class descriptor + t += desc.charAt(i); + if (desc.charAt(i) == ';') { // ';' terminates class + // descriptor + types.add(t); + t = ""; + state = 0; + } + break; + } + } + + if (method) { + // Set up the parameter and return Type fields for a method + + final int sizeM1 = types.size() - 1; + returnType = Type.getType((String) types.get(sizeM1)); + paramTypes = new Type[sizeM1]; + for (i = 0; i < sizeM1; i++) { + final String s = (String) types.get(i); + if (s != null) { + paramTypes[i] = Type.getType(s); + } + } + } else { + if (types.size() != 1) { + throw new IllegalArgumentException("More than one type in " + + "the type descriptor: " + desc); + } + } + } + + /** + * Returns a Type for a primitive type based on its integer "type + * code". + */ + public static Type getType(final int typeCode) { + /* This method is called by codegen.Codegen */ + String desc = null; + + switch (typeCode) { + case BOOLEAN_CODE: + desc = "" + Type.BOOLEAN_CHAR; + break; + case CHARACTER_CODE: + desc = "" + Type.CHARACTER_CHAR; + break; + case FLOAT_CODE: + desc = "" + Type.FLOAT_CHAR; + break; + case DOUBLE_CODE: + desc = "" + Type.DOUBLE_CHAR; + break; + case BYTE_CODE: + desc = "" + Type.BYTE_CHAR; + break; + case SHORT_CODE: + desc = "" + Type.SHORT_CHAR; + break; + case INTEGER_CODE: + desc = "" + Type.INTEGER_CHAR; + break; + case LONG_CODE: + desc = "" + Type.LONG_CHAR; + break; + default: + throw new IllegalArgumentException("Invalid type code: " + typeCode); + } + + Type type = (Type) Type.types.get(desc); + if (type == null) { + type = new Type(); + type.setDesc(desc); + Type.types.put(desc, type); + } + return (type); + } + + /** + * Empty constructor. + */ + private Type() { + this.desc = null; + } + + private void setDesc(final String desc) { + this.desc = desc; + } + + /** + * Returns a Type of a primitive type based on a one-character + * type descriptor. + */ + public static Type getType(final char typeChar) { + String desc = null; + + switch (typeChar) { + case BOOLEAN_CHAR: + case CHARACTER_CHAR: + case FLOAT_CHAR: + case DOUBLE_CHAR: + case BYTE_CHAR: + case SHORT_CHAR: + case INTEGER_CHAR: + case LONG_CHAR: + case ADDRESS_CHAR: + case VOID_CHAR: + desc = "" + typeChar; + desc = desc.intern(); + break; + default: + throw new IllegalArgumentException("Invalid type descriptor: " + + typeChar); + } + + Type type = (Type) Type.types.get(desc); + if (type == null) { + type = new Type(); + type.setDesc(desc); + Type.types.put(desc, type); + } + return (type); + } + + /** + * Get the type code of the type (which must be a primitive type). + * + * @return The type code of the type. + * @exception IllegalArgumentException + * If the type is not primitive. + */ + public int typeCode() { + if (desc.length() == 1) { + switch (desc.charAt(0)) { + case BOOLEAN_CHAR: + return Type.BOOLEAN_CODE; + case CHARACTER_CHAR: + return Type.CHARACTER_CODE; + case FLOAT_CHAR: + return Type.FLOAT_CODE; + case DOUBLE_CHAR: + return Type.DOUBLE_CODE; + case BYTE_CHAR: + return Type.BYTE_CODE; + case SHORT_CHAR: + return Type.SHORT_CODE; + case INTEGER_CHAR: + return Type.INTEGER_CODE; + case LONG_CHAR: + return Type.LONG_CODE; + } + } + + throw new IllegalArgumentException("Invalid type descriptor: " + desc); + } + + /** + * Get a one character name for the type. + */ + public String shortName() { + if (isIntegral()) { + return "" + Type.INTEGER_CHAR; + } + + // Use R rather than L for readability. + if (isReference()) { + return "R"; + } + + Assert.isTrue(desc.length() == 1, "Short name too long: " + desc); + + return desc; + } + + /** + * Get a simplification of the type. All integral types become INTEGER. All + * reference types (objects, arrays, returnAddress) become OBJECT. + * + * @return The simplified type. + */ + public Type simple() { + if (isIntegral()) { + return Type.INTEGER; + } + + if (isReference()) { + return Type.OBJECT; + } + + return this; + } + + /** + * Get a descriptor of the type. + * + * @return The type descriptor. + */ + public String descriptor() { + return desc; + } + + /** + * Check if the type is a method type. + * + * @return true if a method type, false if not. + */ + public boolean isMethod() { + return returnType != null; + } + + /** + * Check if the type is a null type. + * + * @return true if a null type, false if not. + */ + public boolean isNull() { + return equals(Type.NULL); + } + + /** + * Check if the type is a void type. + * + * @return true if a void type, false if not. + */ + public boolean isVoid() { + return equals(Type.VOID); + } + + /** + * Check if the type is a primitive type. + * + * @return true if a primitive type, false if not. + */ + public boolean isPrimitive() { + return !isReference() && !isMethod() && !isVoid(); + } + + /** + * Check if the type is an integral type. Integral contains BOOLEAN as far + * as the JVM is concerned. + * + * @return true if an integral type, false if not. + */ + public boolean isIntegral() { + return (desc.charAt(0) == Type.BYTE_CHAR) + || (desc.charAt(0) == Type.SHORT_CHAR) + || (desc.charAt(0) == Type.INTEGER_CHAR) + || (desc.charAt(0) == Type.CHARACTER_CHAR) + || (desc.charAt(0) == Type.BOOLEAN_CHAR); + } + + /** + * Check if the type is an array type. + * + * @return true if an array type, false if not. + */ + public boolean isArray() { + return desc.charAt(0) == Type.ARRAY_CHAR; + } + + /** + * Check if the type is an object type (not array). + * + * @return true if an object type, false if not. + */ + public boolean isObject() { + return desc.charAt(0) == Type.CLASS_CHAR; + } + + /** + * Check if the type takes of 2 local variable or stack positions. + * + * @return true if a wide type, false if not. + */ + public boolean isWide() { + return (desc.charAt(0) == Type.LONG_CHAR) + || (desc.charAt(0) == Type.DOUBLE_CHAR); + } + + /** + * Check if the type is a returnAddress. + * + * @return true if a address type, false if not. + */ + public boolean isAddress() { + return desc.charAt(0) == Type.ADDRESS_CHAR; + } + + /** + * Check if the type is an array or object. + * + * @return true if a reference type, false if not. + */ + public boolean isReference() { + return (desc.charAt(0) == Type.ARRAY_CHAR) + || (desc.charAt(0) == Type.CLASS_CHAR); + } + + /** + * Get the type descriptor of a class from a string representation.. For + * example "java/lang/String" becomes "Ljava/lang/String;" + * + * @param name + * The name of the class. + * @return The type descriptor of the class. + */ + public static String classDescriptor(String name) { + // The name of the class may arrive as the name of the class file + // in which in resides. In this case, we should remove the .class + // file extension. + if (name.endsWith(".class")) { + name = name.substring(0, name.lastIndexOf('.')); + } + + name = name.replace('.', '/'); + + if (name.charAt(0) == Type.ARRAY_CHAR) { + return name; + } + + return Type.CLASS_CHAR + name + ";"; + } + + /** + * Get the class name of the type. For example if the class descriptor is + * "Ljava/lang/String;", the class name is "java/lang/String". + * + * @return The class name of the type. + */ + public String className() { + if ((desc.charAt(0) == Type.ARRAY_CHAR) || this.isPrimitive()) { + return desc; + } + + return desc.substring(1, desc.lastIndexOf(';')); + } + + /** + * Get the qualifier of the type. For example if the class descriptor is + * "Ljava/lang/String;", the qualifier is "java/lang/". + * + * @return The qualifier of the type. + */ + public String qualifier() { + Assert.isTrue(desc.charAt(0) == Type.CLASS_CHAR, "No qualifier for " + + desc); + + final int index = desc.lastIndexOf('/'); + + if (index >= 0) { + return desc.substring(1, index); + } + + return desc.substring(1, desc.lastIndexOf(';')); + } + + /** + * Get the number of dimensions of an array type. + */ + public int dimensions() { + for (int i = 0; i < desc.length(); i++) { + if (desc.charAt(i) != Type.ARRAY_CHAR) { + return i; + } + } + + throw new IllegalArgumentException(desc + + " does not have an element type."); + } + + /** + * Get a Type representing an array of this type. + */ + public Type arrayType() { + return Type.getType("" + Type.ARRAY_CHAR + desc); + } + + /** + * Create a Type representing a multidimensional array of this type. + */ + public Type arrayType(int dimensions) { + String d = ""; + + while (dimensions-- > 0) { + d += Type.ARRAY_CHAR; + } + + return Type.getType(d + desc); + } + + /** + * Get the element type of this array type. + * + * @param dimensions + * The number of times to index into the array. + * @return The element type. + * @exception IllegalArgumentException + * If the type is not an array. + */ + public Type elementType(final int dimensions) { + for (int i = 0; i < dimensions; i++) { + if (desc.charAt(i) != Type.ARRAY_CHAR) { + throw new IllegalArgumentException(desc + " is not an array"); + } + } + + return Type.getType(desc.substring(dimensions)); + } + + /** + * Get the element type of an array type. + * + * @return The element type. + * @exception IllegalArgumentException + * If the type is not an array. + */ + public Type elementType() { + return elementType(1); + } + + /** + * Get a return type of a method type. + * + * @return The return type. + */ + public Type returnType() { + return this.returnType; + } + + /** + * If this Type is a method type, get the parameter types of the method, + * including empty positions for the second word of wide types. This method + * is good for figuring out which local variables go with parameters. Recall + * that wide data takes up two local variables. + */ + public Type[] indexedParamTypes() { + if (paramTypes == null) { + return null; + } + + final ArrayList p = new ArrayList(paramTypes.length * 2); + + // Count longs and doubles as 2. + for (int i = 0; i < paramTypes.length; i++) { + p.add(paramTypes[i]); + if (paramTypes[i].isWide()) { + p.add(null); + } + } + + final Object[] a = p.toArray(); + final Type[] types = new Type[a.length]; + System.arraycopy(a, 0, types, 0, a.length); + return types; + } + + /** + * Get the parameter types of the method, not including empty positions for + * the second word of wide types. + * + * @return The parameter types. + */ + public Type[] paramTypes() { + return this.paramTypes; + } + + /** + * Returns the number of slots in the stack that this Type takes up on the + * JVM stack. This type descriptor is intended to represent the parameters + * of a method. If there are no parameters, 0 is returned. Else return count + * each parameter as 1 except wide parameters (longs and doubles) as 2. + *

+ * It also give you an idea of how may parameters a method has. + */ + public int stackHeight() { + if (isVoid()) { + return 0; + } + + if (isWide()) { + return 2; + } + + if (paramTypes != null) { + int numParams = 0; + + // Count longs and doubles as 2. + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i].isWide()) { + numParams++; + } + numParams++; + } + + return numParams; + } + + return 1; + } + + /** + * Hash the type. + * + * @return The hash code. + */ + public int hashCode() { + return desc.hashCode(); + } + + /** + * Returns true if two Types are equal. Equal Types + * have equal descriptors. + */ + public boolean equals(final Object obj) { + return (obj != null) && (obj instanceof Type) + && ((Type) obj).desc.equals(desc); + } + + private static Comparator comparator = null; + + /** + * Returns a Comparator used to compare Types. + */ + public static Comparator comparator() { + if (Type.comparator != null) { + return (Type.comparator); + } + + Type.comparator = new Comparator() { + public int compare(final Object o1, final Object o2) { + // o1 < o2 : -1 + // o1 == o2 : 0 + // o1 > o2 : 1 + + if (!(o1 instanceof Type)) { + throw new IllegalArgumentException(o1 + " not a Type"); + } + + if (!(o2 instanceof Type)) { + throw new IllegalArgumentException(o2 + " not a Type"); + } + + final Type t1 = (Type) o1; + final Type t2 = (Type) o2; + + final String d1 = t1.descriptor(); + final String d2 = t2.descriptor(); + + return (d1.compareToIgnoreCase(d2)); + + } + + public boolean equals(final Object o) { + if (o == this) { + return (true); + } else { + return (false); + } + } + }; + return (Type.comparator); + } + + private static final Comparator printComparator = null; + + /** + * Returns a Comparator that compares Types based on + * how they are displayed. + */ + public static Comparator printComparator() { + if (Type.printComparator != null) { + return (Type.printComparator); + } + + Type.comparator = new Comparator() { + public int compare(final Object o1, final Object o2) { + // o1 < o2 : -1 + // o1 == o2 : 0 + // o1 > o2 : 1 + + if (!(o1 instanceof Type)) { + throw new IllegalArgumentException(o1 + " not a Type"); + } + + if (!(o2 instanceof Type)) { + throw new IllegalArgumentException(o2 + " not a Type"); + } + + final Type t1 = (Type) o1; + final Type t2 = (Type) o2; + + final String d1 = t1.toString(); + final String d2 = t2.toString(); + + return (d1.compareToIgnoreCase(d2)); + + } + + public boolean equals(final Object o) { + if (o == this) { + return (true); + } else { + return (false); + } + } + }; + return (Type.comparator); + } + + /** + * Returns a string representing the truncated name of a Type. + * For instance java/lang/String would just return + * String. + */ + public static String truncatedName(final Type type) { + // Sometimes type can be null! I don't know why. + if (type == null) { + return (""); + } + + // System.out.println("Truncating type: " + type.desc); + + if (type.isPrimitive() || type.isVoid() || type.isNull()) { + return (type.desc); // Don't invoke toString! + } + + if (type.isArray()) { + return ("[" + Type.truncatedName(type.elementType())); + } + + if (type.isMethod()) { + final StringBuffer sb = new StringBuffer(); + sb.append('('); + final Type[] params = type.indexedParamTypes(); + for (int i = 0; i < params.length; i++) { + sb.append(Type.truncatedName(params[i])); + if (i < params.length - 1) { + sb.append(','); + } + } + sb.append(')'); + sb.append(Type.truncatedName(type.returnType())); + return (sb.toString()); + } + + // Otherwise its a normal type such as java/lang/String. + // First, chop off the package name. + final String longName = type.className(); + int lastSlash = longName.lastIndexOf('/'); + if (lastSlash == -1) { + lastSlash = longName.lastIndexOf('.'); + } + final String className = longName.substring(lastSlash + 1); + + if (className.length() > 8) { + // Come up with an abbreviated name. Find each capital letter + // in the name. If there are fewer than 8 capital letters, then + // use characters after the last capital for a total of 8 + // characters. For instance, StringBuffer becomes SBuffer. + + // Count capitals + final StringBuffer caps = new StringBuffer(); + final int nameLength = className.length(); + for (int i = 0; i < nameLength; i++) { + final char c = className.charAt(i); + if (Character.isUpperCase(c)) { + caps.append(c); + } + } + + if (caps.length() > 8) { + // This is an UGLY class name. Use the first four caps and + // the last four caps. + final int capsLength = caps.length(); + for (int i = 4; i < capsLength - 4; i++) { + caps.deleteCharAt(i); + } + return (caps.toString()); + } + + if (caps.length() <= 0) { + // Grumble. No caps in name. Return first 8 characters + return (className.substring(0, 8)); + } + + final char lastCap = caps.charAt(caps.length() - 1); + final int indexLastCap = className.lastIndexOf(lastCap); + final int capsLength = caps.length(); + int end; + + if (capsLength + (nameLength - indexLastCap) > 8) { + end = indexLastCap + 1 + (8 - capsLength); + } else { + end = nameLength; + } + + final String endOfName = className.substring(indexLastCap + 1, end); + + caps.append(endOfName); + return (caps.toString()); + } + + // Just return name of class + return (className); + + } + + /** + * Convert the type to a string. + * + * @return A string representation of the type. + */ + public String toString() { + if (Type.PRINT_TRUNCATED) { + return (Type.truncatedName(this)); + } else { + return desc; + } + } + + /** + * Test truncatedName. + */ + public static void main(final String args[]) { + Type.truncatedName(Type.getType("(D)V")); + + // Print the truncated name of each argument + for (int i = 0; i < args.length; i++) { + System.out.println("Truncated name of " + args[i] + ": " + + Type.truncatedName(Type.getType(args[i]))); + + } + } +} diff --git a/src/EDU/purdue/cs/bloat/editor/TypeComparator.java b/src/EDU/purdue/cs/bloat/editor/TypeComparator.java new file mode 100644 index 0000000..2955d36 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/TypeComparator.java @@ -0,0 +1,120 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.util.*; + +// // For testing only +// import EDU.purdue.cs.bloat.file.*; +// import EDU.purdue.cs.bloat.context.*; + +/** + * A TypeComparator orders Types such that a subclass + * preceededs its superclass. Note that this doesn't really work with + * interfaces. + */ +public final class TypeComparator implements Comparator { + + public static boolean DEBUG = false; + + private EditorContext context; + + private static void db(final String s) { + if (TypeComparator.DEBUG) { + System.out.println(s); + } + } + + /** + * Constructor. + */ + public TypeComparator(final EditorContext context) { + this.context = context; + } + + /** + * Returns a negative value if o1 < o2 (t1 is a subclass of t2). Otherwise, + * it returns a positive value. + */ + public int compare(final Object o1, final Object o2) { + Assert.isTrue(o1 instanceof Type, o1 + " is not a Type"); + Assert.isTrue(o2 instanceof Type, o2 + " is not a Type"); + + final Type t1 = (Type) o1; + final Type t2 = (Type) o2; + + TypeComparator.db("Comparing " + t1 + " to " + t2); + + final ClassHierarchy hier = context.getHierarchy(); + + if (hier.subclassOf(t1, t2)) { + TypeComparator.db(" " + t1 + " is a subclass of " + t2); + return (-1); + + } else if (hier.subclassOf(t2, t1)) { + TypeComparator.db(" " + t2 + " is a subclass of " + t1); + return (1); + + } else { + TypeComparator.db(" " + t1 + " and " + t2 + " are unrelated"); + + // Don't return 0. If you do, the type will not get included in + // the sorted set. Weird. + return (1); + } + } + + /** + * Indicates whether some other object is "equal to" this Comparator. + */ + public boolean equals(final Object other) { + return (other instanceof TypeComparator); + } + + // /** + // * Test program. Reads class names from the command line. + // */ + // public static void main(String[] args) { + // // Make a list of class names + // List names = new ArrayList(); + // for(int i = 0; i < args.length; i++) { + // names.add(args[i]); + // } + + // // Do some BLOAT magic + // EditorContext context = + // new CachingBloatContext(new ClassFileLoader(), names, false); + // Collection classes = context.getHierarchy().classes(); + + // TypeComparator.DEBUG = true; + // SortedSet sorted = new TreeSet(new TypeComparator(context)); + // sorted.addAll(classes); + + // System.out.println(classes.size() + " classes"); + // System.out.println(sorted.size() + " sorted types:"); + // Object[] array = sorted.toArray(); + // for(int i = 0; i < array.length; i++) { + // System.out.println(" " + array[i]); + // } + // } +} diff --git a/src/EDU/purdue/cs/bloat/editor/UseMap.java b/src/EDU/purdue/cs/bloat/editor/UseMap.java new file mode 100644 index 0000000..ad75316 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/editor/UseMap.java @@ -0,0 +1,53 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.editor; + +import java.util.*; + +import EDU.purdue.cs.bloat.tree.*; + +public class UseMap { + + public Hashtable map; + + public UseMap() { + map = new Hashtable(); + } + + public void add(final LocalExpr use, final Instruction inst) { + + final Node def = use.def(); + if (def != null) { + map.put(inst, def); + } + } + + public boolean hasDef(final Instruction inst) { + + return map.containsKey(inst); + } + + public boolean hasSameDef(final Instruction a, final Instruction b) { + return map.containsKey(a) && map.containsKey(b) + && (map.get(a) == map.get(b)); + } + +} 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/file/.cvsignore b/src/EDU/purdue/cs/bloat/file/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/file/Attribute.java b/src/EDU/purdue/cs/bloat/file/Attribute.java new file mode 100644 index 0000000..9414e30 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Attribute.java @@ -0,0 +1,89 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +/** + * Attribute is an abstract class for an attribute defined for a method, field, + * or class. An attribute consists of its name (represented as an index into the + * constant pool) and its length. Attribute is extended to represent a constant + * value, code, exceptions, etc. + * + * @see Code + * @see ConstantValue + * @see Exceptions + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public abstract class Attribute { + protected int nameIndex; + + protected int length; + + /** + * Constructor. + * + * @param nameIndex + * The index into the constant pool of the name of the attribute. + * @param length + * The length of the attribute, excluding the header. + */ + public Attribute(final int nameIndex, final int length) { + this.nameIndex = nameIndex; + this.length = length; + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + */ + public abstract void writeData(DataOutputStream out) throws IOException; + + /** + * Returns a string representation of the attribute. + */ + public String toString() { + return "(attribute " + nameIndex + " " + length + ")"; + } + + /** + * Returns the index into the constant pool of the name of the attribute. + */ + public int nameIndex() { + return nameIndex; + } + + /** + * Returns the length of the attribute, excluding the header. + */ + public int length() { + return length; + } + + public Object clone() { + throw new UnsupportedOperationException("Cannot clone Attribute! " + + " (subclass: " + this.getClass() + ")"); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/ClassFile.java b/src/EDU/purdue/cs/bloat/file/ClassFile.java new file mode 100644 index 0000000..3b8c0b2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/ClassFile.java @@ -0,0 +1,976 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * ClassFile basically represents a Java classfile as it is found on disk. The + * classfile is modeled according to the Java Virtual Machine Specification. + * Methods are provided to edit the classfile at a very low level. + * + * @see Attribute + * @see EDU.purdue.cs.bloat.reflect.Constant + * @see Field + * @see Method + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class ClassFile implements ClassInfo { + private ClassInfoLoader loader; // ClassInfoLoader that "owns" this class + + private List constants; // The constant pool + + private int modifiers; // This class's modifer bit field + + private int thisClass; + + private int superClass; + + private int[] interfaces; + + private Field[] fields; + + private Method[] methods; + + private Attribute[] attrs; + + private File file; // (.class) File in which this class resides + + /** + * Constructor. This constructor parses the class file from the input + * stream. + * + * @param file + * The file in which the class resides. + * @param loader + * The class info loader which loaded the class. + * @param in + * The data stream containing the class. + * @exception ClassFormatError + * When the class could not be parsed. + */ + public ClassFile(final File file, final ClassInfoLoader loader, + final DataInputStream in) { + this.loader = loader; + this.file = file; + // Assert.isTrue(file != null, "Null file for class file"); + + // Read in file contents from stream + try { + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading header"); + } + readHeader(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading constant pool"); + } + readConstantPool(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading access flags"); + } + readAccessFlags(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading class info"); + } + readClassInfo(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading fields"); + } + readFields(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading methods"); + } + readMethods(in); + + if (ClassFileLoader.DEBUG) { + System.out.println("ClassFile: Reading Attributes"); + } + readAttributes(in); + in.close(); + } catch (final IOException e) { + throw new ClassFormatException(e.getMessage() + " (in file " + file + + ")"); + } + } + + /** + * Creates a new ClassFile from scratch. It has no fields or + * methods. + * + * @param modifiers + * The modifiers describing the newly-created class + * @param classIndex + * The index of the type of the newly-created class in its + * constant pool + * @param superClassIndex + * The index of the type of the newly-created class's superclass + * in its constant pool + * @param interfaceIndexes + * The indexes of the types 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). + */ + public ClassFile(final int modifiers, final int classIndex, + final int superClassIndex, final int[] interfaceIndexes, + final List constants, final ClassInfoLoader loader) { + this.modifiers = modifiers; + this.thisClass = classIndex; + this.superClass = superClassIndex; + this.interfaces = interfaceIndexes; + this.constants = constants; + this.loader = loader; + + this.fields = new Field[0]; + this.methods = new Method[0]; + this.attrs = new Attribute[0]; + } + + /** + * Get the class info loader for the class. + * + * @return The class info loader for the class. + */ + public ClassInfoLoader loader() { + return loader; + } + + /** + * Get the name of the class, including the package name. + * + * @return The name of the class. + */ + public String name() { + Constant c = (Constant) constants.get(thisClass); + Assert.isNotNull(c, "Null constant for class name"); + if (c.tag() == Constant.CLASS) { + final Integer nameIndex = (Integer) c.value(); + if (nameIndex != null) { + c = (Constant) constants.get(nameIndex.intValue()); + if (c.tag() == Constant.UTF8) { + return (String) c.value(); + } + } + } + + throw new ClassFormatException("Couldn't find class name in file"); + } + + /** + * Set the index into the constant pool of the name of the class. + * + * @param index + * The index of the name of the class. + */ + public void setClassIndex(final int index) { + this.thisClass = index; + } + + /** + * Set the index into the constant pool of the name of the class's + * superclass. + * + * @param index + * The index of the name of the superclass. + */ + public void setSuperclassIndex(final int index) { + this.superClass = index; + } + + /** + * Set the indices into the constant pool of the names of the class's + * interfaces. + * + * @param indices + * The indices of the names of the interfaces. + */ + public void setInterfaceIndices(final int[] indices) { + this.interfaces = indices; + } + + /** + * Get the index into the constant pool of the name of the class. + * + * @return The index of the name of the class. + */ + public int classIndex() { + return thisClass; + } + + /** + * Get the index into the constant pool of the name of the class's + * superclass. + * + * @return The index of the name of the superclass. + */ + public int superclassIndex() { + return superClass; + } + + /** + * Get the indices into the constant pool of the names of the class's + * interfaces. + * + * @return The indices of the names of the interfaces. + */ + public int[] interfaceIndices() { + return interfaces; + } + + /** + * Set the modifiers of the class. The values correspond to the constants in + * the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the class. + * @see Modifiers + */ + public void setModifiers(final int modifiers) { + this.modifiers = modifiers; + } + + /** + * Get the modifiers of the class. The values correspond to the constants in + * the Modifiers class. + * + * @return A bit vector of modifier flags for the class. + * @see Modifiers + */ + public int modifiers() { + return modifiers; + } + + /** + * Get an array of FieldInfo structures for each field in the class. + * + * @return An array of FieldInfo structures. + */ + public FieldInfo[] fields() { + return fields; + } + + /** + * Returns an array of MethodInfo structures for each method in the class. + */ + public MethodInfo[] methods() { + return methods; + } + + /** + * Sets the methods in this class. + */ + public void setMethods(final MethodInfo[] methods) { + this.methods = new Method[methods.length]; + for (int i = 0; i < methods.length; i++) { + this.methods[i] = (Method) methods[i]; + } + + } + + /** + * Get an array of the constants in the constant pool. + * + * @return An array of Constants. + */ + public Constant[] constants() { + return (Constant[]) constants.toArray(new Constant[0]); + } + + /** + * Set all the constants in the constant pool. + * + * @param constants + * The array of Constants. + */ + public void setConstants(final Constant[] constants) { + this.constants = new ArrayList(constants.length); + for (int i = 0; i < constants.length; i++) { + this.constants.add(i, constants[i]); + } + } + + /** + * Returns the File from which this ClassFile was created. If + * this ClassFile was created from scratch, null + * is returned. + */ + public File file() { + return file; + } + + /** + * Creates a new File object to hold this class. It is placed in the output + * directory and has the name of the class represented by this ClassFile + * followed by the .class extension. + */ + public File outputFile() { + final File outputDir = ((ClassFileLoader) loader).outputDir(); + final String fileName = this.name().replace('/', File.separatorChar); + return new File(outputDir, fileName + ".class"); + } + + /** + * Commit any changes back to a file in the output directory. The output + * directory is determined from the ClassFileLoader. + */ + public void commit() { + try { + commitTo(loader.outputStreamFor(this)); + + } catch (final IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Commit changes made to this class. Write changes to an OutputStream. + */ + void commitTo(final OutputStream outStream) { + try { + final DataOutputStream out = new DataOutputStream(outStream); + + writeHeader(out); + writeConstantPool(out); + writeAccessFlags(out); + writeClassInfo(out); + writeFields(out, null); + writeMethods(out, null); + writeAttributes(out); + + out.close(); + + } catch (final IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + public void commitOnly(final Set methods, final Set fields) { + try { + final OutputStream outStream = loader.outputStreamFor(this); + final DataOutputStream out = new DataOutputStream(outStream); + + writeHeader(out); + writeConstantPool(out); + writeAccessFlags(out); + writeClassInfo(out); + writeFields(out, fields); + writeMethods(out, methods); + writeAttributes(out); + + out.close(); + + } catch (final IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + /** + * Write the class file header. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeHeader(final DataOutputStream out) throws IOException { + out.writeInt(0xCAFEBABE); + out.writeShort(3); + out.writeShort(45); + } + + /** + * Write the class's constant pool. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeConstantPool(final DataOutputStream out) + throws IOException { + out.writeShort(constants.size()); + + // Write the constants. The first constant is reserved for + // internal use by the JVM, so start at 1. + for (int i = 1; i < constants.size(); i++) { + writeConstant(out, (Constant) constants.get(i)); + + switch (((Constant) constants.get(i)).tag()) { + case Constant.LONG: + case Constant.DOUBLE: + // Longs and doubles take up 2 constant pool entries. + i++; + break; + } + } + } + + /** + * Read a constant from the constant pool. + * + * @param in + * The stream from which to read. + * @return The constant. + * @exception IOException + * If an error occurs while reading. + */ + private Constant readConstant(final DataInputStream in) throws IOException { + final int tag = in.readUnsignedByte(); + Object value; + + switch (tag) { + case Constant.CLASS: + case Constant.STRING: + value = new Integer(in.readUnsignedShort()); + break; + case Constant.FIELD_REF: + case Constant.METHOD_REF: + case Constant.INTERFACE_METHOD_REF: + case Constant.NAME_AND_TYPE: + value = new int[2]; + ((int[]) value)[0] = in.readUnsignedShort(); + ((int[]) value)[1] = in.readUnsignedShort(); + break; + case Constant.INTEGER: + value = new Integer(in.readInt()); + break; + case Constant.FLOAT: + value = new Float(in.readFloat()); + break; + case Constant.LONG: + // Longs take up 2 constant pool entries. + value = new Long(in.readLong()); + break; + case Constant.DOUBLE: + // Doubles take up 2 constant pool entries. + value = new Double(in.readDouble()); + break; + case Constant.UTF8: + value = in.readUTF(); + break; + default: + throw new ClassFormatException(file.getPath() + + ": Invalid constant tag: " + tag); + } + + return new Constant(tag, value); + } + + /** + * Write a constant in the constant pool. + * + * @param out + * The stream to which to write. + * @param constant + * The constant. + * @exception IOException + * If an error occurs while writing. + */ + private void writeConstant(final DataOutputStream out, + final Constant constant) throws IOException { + final int tag = constant.tag(); + final Object value = constant.value(); + + out.writeByte(tag); + + switch (tag) { + case Constant.CLASS: + case Constant.STRING: + out.writeShort(((Integer) value).intValue()); + break; + case Constant.INTEGER: + out.writeInt(((Integer) value).intValue()); + break; + case Constant.FLOAT: + out.writeFloat(((Float) value).floatValue()); + break; + case Constant.LONG: + out.writeLong(((Long) value).longValue()); + break; + case Constant.DOUBLE: + out.writeDouble(((Double) value).doubleValue()); + break; + case Constant.UTF8: + out.writeUTF((String) value); + break; + case Constant.FIELD_REF: + case Constant.METHOD_REF: + case Constant.INTERFACE_METHOD_REF: + case Constant.NAME_AND_TYPE: + out.writeShort(((int[]) value)[0]); + out.writeShort(((int[]) value)[1]); + break; + } + } + + /** + * Write the class's access flags. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeAccessFlags(final DataOutputStream out) + throws IOException { + out.writeShort(modifiers); + } + + /** + * Write the class's name, superclass, and interfaces. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeClassInfo(final DataOutputStream out) throws IOException { + out.writeShort(thisClass); + out.writeShort(superClass); + + out.writeShort(interfaces.length); + + for (int i = 0; i < interfaces.length; i++) { + out.writeShort(interfaces[i]); + } + } + + /** + * Write the class's fields. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeFields(final DataOutputStream out, final Set onlyFields) + throws IOException { + out.writeShort(fields.length); + + for (int i = 0; i < fields.length; i++) { + if ((onlyFields != null) && onlyFields.contains(fields[i])) { + continue; + } + fields[i].write(out); + } + } + + /** + * Write the class's methods. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeMethods(final DataOutputStream out, final Set onlyMethods) + throws IOException { + if (onlyMethods != null) { + out.writeShort(onlyMethods.size()); + + } else { + if (Method.DEBUG) { + System.out.println("Writing " + methods.length + " methods"); + } + out.writeShort(methods.length); + } + + for (int i = 0; i < methods.length; i++) { + if ((onlyMethods != null) && onlyMethods.contains(methods[i])) { + continue; + } + methods[i].write(out); + } + } + + /** + * Write the class's attributes. No attributes are written by this method + * since none are required. + * + * @param out + * The stream to which to write. + * @exception IOException + * If an error occurs while writing. + */ + private void writeAttributes(final DataOutputStream out) throws IOException { + out.writeShort(attrs.length); + + for (int i = 0; i < attrs.length; i++) { + out.writeShort(attrs[i].nameIndex()); + out.writeInt(attrs[i].length()); + attrs[i].writeData(out); + } + } + + /** + * Read the class file header. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readHeader(final DataInputStream in) throws IOException { + final int magic = in.readInt(); + + if (magic != 0xCAFEBABE) { + throw new ClassFormatError("Bad magic number."); + } + + in.readUnsignedShort(); // major + in.readUnsignedShort(); // minor + } + + /** + * Read the class's constant pool. Constants in the constant pool are + * modeled by an array of reflect.Constant/ + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + * + * @see EDU.purdue.cs.bloat.reflect.Constant + * @see #constants + */ + private void readConstantPool(final DataInputStream in) throws IOException { + final int count = in.readUnsignedShort(); + + constants = new ArrayList(count); + + // The first constant is reserved for internal use by the JVM. + constants.add(0, null); + + // Read the constants. + for (int i = 1; i < count; i++) { + constants.add(i, readConstant(in)); + + switch (((Constant) constants.get(i)).tag()) { + case Constant.LONG: + case Constant.DOUBLE: + // Longs and doubles take up 2 constant pool entries. + constants.add(++i, null); + break; + } + } + } + + /** + * Read the class's access flags. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readAccessFlags(final DataInputStream in) throws IOException { + modifiers = in.readUnsignedShort(); + } + + /** + * Read the class's name, superclass, and interfaces. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readClassInfo(final DataInputStream in) throws IOException { + thisClass = in.readUnsignedShort(); + superClass = in.readUnsignedShort(); + + final int numInterfaces = in.readUnsignedShort(); + + interfaces = new int[numInterfaces]; + + for (int i = 0; i < numInterfaces; i++) { + interfaces[i] = in.readUnsignedShort(); + } + } + + /** + * Read the class's fields. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readFields(final DataInputStream in) throws IOException { + final int numFields = in.readUnsignedShort(); + + fields = new Field[numFields]; + + for (int i = 0; i < numFields; i++) { + fields[i] = new Field(in, this); + } + } + + /** + * Read the class's methods. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readMethods(final DataInputStream in) throws IOException { + final int numMethods = in.readUnsignedShort(); + + methods = new Method[numMethods]; + + for (int i = 0; i < numMethods; i++) { + methods[i] = new Method(in, this); + } + } + + /** + * Read the class's attributes. Since none of the attributes are required, + * just read the length of each attribute and skip that many bytes. + * + * @param in + * The stream from which to read. + * @exception IOException + * If an error occurs while reading. + */ + private void readAttributes(final DataInputStream in) throws IOException { + final int numAttributes = in.readUnsignedShort(); + + attrs = new Attribute[numAttributes]; + + for (int i = 0; i < numAttributes; i++) { + final int nameIndex = in.readUnsignedShort(); + final int length = in.readInt(); + attrs[i] = new GenericAttribute(in, nameIndex, length); + } + } + + /** + * Creates a new field in this classfile + */ + public FieldInfo addNewField(final int modifiers, final int typeIndex, + final int nameIndex) { + final Field field = new Field(this, modifiers, typeIndex, nameIndex); + + // Add the new field to the list of fields + final Field[] fields = new Field[this.fields.length + 1]; + for (int i = 0; i < this.fields.length; i++) { + fields[i] = this.fields[i]; + } + fields[this.fields.length] = field; + this.fields = fields; + + return (field); + } + + /** + * Creates a new field in this classfile + */ + public FieldInfo addNewField(final int modifiers, final int typeIndex, + final int nameIndex, final int cvNameIndex, + final int constantValueIndex) { + final Field field = new Field(this, modifiers, typeIndex, nameIndex, + cvNameIndex, constantValueIndex); + + // Add the new field to the list of fields + final Field[] fields = new Field[this.fields.length + 1]; + for (int i = 0; i < this.fields.length; i++) { + fields[i] = this.fields[i]; + } + fields[this.fields.length] = field; + this.fields = fields; + + return (field); + } + + /** + * Removes the field whose name is at the given index in the constant pool. + * + * @throws IllegalArgumentException The class does not contain a field whose + * name is at the given index + */ + public void deleteField(final int nameIndex) { + final List newFields = new ArrayList(); + + boolean foundIt = false; + for (int i = 0; i < this.fields.length; i++) { + final Field field = this.fields[i]; + if (field.nameIndex() == nameIndex) { + foundIt = true; + + } else { + newFields.add(field); + } + } + + if (!foundIt) { + final String s = "No field with name index " + nameIndex + " in " + + this.name(); + throw new IllegalArgumentException(s); + + } else { + this.fields = (Field[]) newFields.toArray(new Field[0]); + } + } + + /** + * Deletes a method from this class + * + * @param nameIndex + * Index in the constant pool of the name of the method to be + * deleted + * @param typeIndex + * Index in the constant pool of the type of the method to be + * deleted + * + * @throws IllegalArgumentException The class modeled by this + * ClassInfo does not contain a method whose name and + * type are not at the given indices + */ + public void deleteMethod(final int nameIndex, final int typeIndex) { + final List newMethods = new ArrayList(); + + boolean foundIt = false; + for (int i = 0; i < this.methods.length; i++) { + final Method method = this.methods[i]; + if ((method.nameIndex() == nameIndex) + && (method.typeIndex() == typeIndex)) { + foundIt = true; + + } else { + newMethods.add(method); + } + } + + if (!foundIt) { + final String s = "No method with name index " + nameIndex + + " and type index " + typeIndex + " in " + this.name(); + throw new IllegalArgumentException(s); + + } else { + this.methods = (Method[]) newMethods.toArray(new Method[0]); + } + } + + /** + * Creates a new method in this class + */ + public MethodInfo addNewMethod(final int modifiers, final int typeIndex, + final int nameIndex, final int exceptionIndex, + final int[] exceptionTypeIndices, final int codeIndex) { + final Exceptions exceptions = new Exceptions(this, exceptionIndex, + exceptionTypeIndices); + final Code code = new Code(this, codeIndex); // code can't be null + final Attribute[] attributes = new Attribute[] { exceptions, code }; + + final Method method = new Method(this, modifiers, nameIndex, typeIndex, + attributes, code, exceptions); + + // Add the new method to the list of method + final Method[] methods = new Method[this.methods.length + 1]; + for (int i = 0; i < this.methods.length; i++) { + methods[i] = this.methods[i]; + } + methods[this.methods.length] = method; + this.methods = methods; + + return (method); + } + + /** + * Prints a textual representation of this classfile to a PrintStream. The + * text includes information such as the constants in the constant pool, the + * name of the class's superclass, its modifier flags, its fields, and its + * methods. + * + * @param out + * The stream to which to print. + */ + public void print(final PrintStream out) { + print(new PrintWriter(out, true)); + } + + public void print(final PrintWriter out) { + out.print("(constants"); + for (int i = 0; i < constants.size(); i++) { + out.print("\n " + i + ": " + constants.get(i)); + } + out.println(")"); + + out.println("(class " + classIndex() + ")"); + out.println("(super " + superclassIndex() + ")"); + + out.print("(interfaces"); + for (int i = 0; i < interfaces.length; i++) { + out.print("\n " + i + ": " + interfaces[i]); + } + out.println(")"); + + out.print("(modifiers"); + if ((modifiers & Modifiers.PUBLIC) != 0) { + out.print(" PUBLIC"); + } + if ((modifiers & Modifiers.FINAL) != 0) { + out.print(" FINAL"); + } + if ((modifiers & Modifiers.SUPER) != 0) { + out.print(" SUPER"); + } + if ((modifiers & Modifiers.INTERFACE) != 0) { + out.print(" INTERFACE"); + } + if ((modifiers & Modifiers.ABSTRACT) != 0) { + out.print(" ABSTRACT"); + } + out.println(")"); + + out.print("(fields"); + for (int i = 0; i < fields.length; i++) { + out.print("\n " + i + ": " + fields[i]); + } + out.println(")"); + + out.print("(methods"); + for (int i = 0; i < methods.length; i++) { + out.print("\n " + i + ": " + methods[i]); + } + out.println(")"); + } + + public String toString() { + return "(ClassFile " + name() + ")"; + } +} diff --git a/src/EDU/purdue/cs/bloat/file/ClassFileLoader.java b/src/EDU/purdue/cs/bloat/file/ClassFileLoader.java new file mode 100644 index 0000000..bea56a0 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/ClassFileLoader.java @@ -0,0 +1,463 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * ClassFileLoder provides an interface for loading classes from files. The + * actual loading is done by the ClassFile itself. + *

+ * Classes may be specified by their full package name (java.lang.String), + * or by the name of their class file (myclasses/Test.class). The + * class path may contain directories or Zip or Jar files. Any classes that are + * written back to disk ("committed") are placed in the output directory. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class ClassFileLoader implements ClassInfoLoader { + public static boolean DEBUG = false; + + public static boolean USE_SYSTEM_CLASSES = true; + + private File outputDir; // Directory in which to write committed class files + + private String classpath; // Path to search for classes + + private Map openZipFiles; // zip files to search for class files + + private LinkedList cache; // We keep a cache of CACHE_LIMIT class files + + private boolean verbose; + + private static final int CACHE_LIMIT = 10; + + /** + * Constructor. The classpath initially consists of the contents of the + * java.class.path and sun.boot.class.path system + * properties. + */ + public ClassFileLoader() { + outputDir = new File("."); + classpath = System.getProperty("java.class.path"); + classpath += File.pathSeparator + + System.getProperty("sun.boot.class.path"); + if (ClassFileLoader.USE_SYSTEM_CLASSES) { + classpath += File.pathSeparator + + System.getProperty("java.sys.class.path"); + } + openZipFiles = new HashMap(); + cache = new LinkedList(); + verbose = false; + } + + public void setVerbose(final boolean verbose) { + this.verbose = verbose; + } + + /** + * Sets the classpath. + */ + public void setClassPath(final String classpath) { + this.classpath = classpath; + } + + /** + * Adds to the classpath (CLASSPATH = CLASSPATH + morePath). + */ + public void appendClassPath(final String morePath) { + this.classpath += File.pathSeparator + morePath; + } + + /** + * Adds to the classpath (CLASSPATH = morePath + CLASSPATH). + */ + public void prependClassPath(final String morePath) { + this.classpath = morePath + File.pathSeparator + this.classpath; + } + + /** + * Returns the path used to search for class files. + */ + public String getClassPath() { + return (this.classpath); + } + + /** + * Load the class from a stream. + * + * @param inputFile + * The file from which to load the class. + * @param stream + * The stream from which to load the class. + * @return A ClassInfo for the class. + * @exception ClassNotFoundException + * The class cannot be found in the class path. + */ + private ClassInfo loadClassFromStream(final File inputFile, + final InputStream stream) throws ClassNotFoundException { + + final DataInputStream in = new DataInputStream(stream); + final ClassFile file = new ClassFile(inputFile, this, in); + + return file; + } + + /** + * Load the class from the file. + * + * @param file + * The File from which to load a class. + * @return A ClassInfo for the class. + * @exception ClassNotFoundException + * The class cannot be found in the class path. + */ + private ClassInfo loadClassFromFile(final File file) + throws ClassNotFoundException { + try { + final InputStream in = new FileInputStream(file); + + final ClassInfo info = loadClassFromStream(file, in); + + if (verbose) { + System.out.println("[Loaded " + info.name() + " from " + + file.getPath() + "]"); + } + + try { + in.close(); + } catch (final IOException ex) { + } + + return info; + } catch (final FileNotFoundException e) { + throw new ClassNotFoundException(file.getPath()); + } + } + + /** + * Loads all of the classes that are contained in a zip (or jar) file. + * Returns an array of the ClassInfos for the classes in the zip + * file. + */ + public ClassInfo[] loadClassesFromZipFile(final ZipFile zipFile) + throws ClassNotFoundException { + final ClassInfo[] infos = new ClassInfo[zipFile.size()]; + + // Examine each entry in the zip file + final Enumeration entries = zipFile.entries(); + for (int i = 0; entries.hasMoreElements(); i++) { + final ZipEntry entry = (ZipEntry) entries.nextElement(); + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + + try { + final InputStream stream = zipFile.getInputStream(entry); + final File file = new File(entry.getName()); + + infos[i] = loadClassFromStream(file, stream); + + } catch (final IOException ex) { + System.err.println("IOException: " + ex); + } + } + + return (infos); + } + + public ClassInfo newClass(final int modifiers, final int classIndex, + final int superClassIndex, final int[] interfaceIndexes, + final List constants) { + return new ClassFile(modifiers, classIndex, superClassIndex, + interfaceIndexes, constants, this); + } + + /** + * Loads the class with the given name. Searches the class path, including + * zip files, for the class and then returns a data stream for the class + * file. + * + * @param name + * The name of the class to load, including the package name. + * @return A ClassInfo for the class. + * @exception ClassNotFoundException + * The class cannot be found in the class path. + */ + public ClassInfo loadClass(String name) throws ClassNotFoundException { + ClassInfo file = null; + + // Check to see if name ends with ".class". If so, load the class from + // that file. Note that this is okay because we can never have a class + // named "class" (i.e. a class named "class" with a lower-case 'c' can + // never be specified in a fully-specified java class name) because + // "class" is a reserved word. + + if (name.endsWith(".class")) { + final File nameFile = new File(name); + + if (!nameFile.exists()) { + throw new ClassNotFoundException(name); + + } else { + return (loadClassFromFile(nameFile)); + } + } + + // Otherwise, we have a (possibly fully-specified) class name. + name = name.replace('.', '/'); + + // Check the cache for the class file. + if (ClassFileLoader.DEBUG) { + System.out + .println(" Looking for " + name + " in cache = " + cache); + } + + final Iterator iter = cache.iterator(); + + while (iter.hasNext()) { + file = (ClassFile) iter.next(); + + if (name.equals(file.name())) { + if (ClassFileLoader.DEBUG) { + System.out.println(" Found " + file.name() + " in cache"); + } + + // Move to the front of the cache. + iter.remove(); + cache.addFirst(file); + + return file; + } + } + + file = null; + + final String classFile = name.replace('/', File.separatorChar) + + ".class"; + + // For each entry in the class path, search zip files and directories + // for classFile. When found, open an InputStream and break + // out of the loop to read the class file. + final String path = classpath + File.pathSeparator; + + if (ClassFileLoader.DEBUG) { + System.out.println("CLASSPATH = " + path); + } + + int index = 0; + int end = path.indexOf(File.pathSeparator, index); + + SEARCH: while (end >= 0) { + final String dir = path.substring(index, end); + + File f = new File(dir); + + if (f.isDirectory()) { + // The directory is really a directory. If the class file + // exists, open a stream and return. + f = new File(dir, classFile); + + if (f.exists()) { + try { + final InputStream in = new FileInputStream(f); + + if (verbose) { + System.out.println(" [Loaded " + name + " from " + + f.getPath() + "]"); + } + + file = loadClassFromStream(f, in); + + try { + in.close(); + + } catch (final IOException ex) { + } + + break SEARCH; + + } catch (final FileNotFoundException ex) { + } + } + + } else if (dir.endsWith(".zip") || dir.endsWith(".jar")) { + // Maybe a zip file? + try { + ZipFile zip = (ZipFile) openZipFiles.get(dir); + + if (zip == null) { + zip = new ZipFile(f); + openZipFiles.put(dir, zip); + } + + final String zipEntry = classFile.replace( + File.separatorChar, '/'); + + final ZipEntry entry = zip.getEntry(zipEntry); + + if (entry != null) { + // Found the class file in the zip file. + // Open a stream and return. + if (verbose) { + System.out.println(" [Loaded " + name + " from " + + f.getPath() + "]"); + } + + final InputStream in = zip.getInputStream(entry); + file = loadClassFromStream(f, in); + + try { + in.close(); + + } catch (final IOException ex) { + } + break SEARCH; + } + } catch (final ZipException ex) { + } catch (final IOException ex) { + } + } + + index = end + 1; + end = path.indexOf(File.pathSeparator, index); + } + + if (file == null) { + // The class file wasn't in the class path. Try the currnet + // directory. If not there, give up. + final File f = new File(classFile); + + if (!f.exists()) { + throw new ClassNotFoundException(name); + } + + if (verbose) { + System.out.println(" [Loaded " + name + " from " + f.getPath() + + "]"); + } + + try { + final InputStream in = new FileInputStream(f); + file = loadClassFromStream(f, in); + + try { + in.close(); + } catch (final IOException ex) { + } + } catch (final FileNotFoundException ex) { + throw new ClassNotFoundException(name); + } + } + + if (file == null) { + throw new ClassNotFoundException(name); + } + + // If we've reached the cache size limit, remove the oldest file + // in the cache. Then add the new file. + if (cache.size() == ClassFileLoader.CACHE_LIMIT) { + cache.removeLast(); + } + + cache.addFirst(file); + + return file; + } + + /** + * Set the directory into which commited class files should be written. + * + * @param dir + * The directory. + */ + public void setOutputDir(final File dir) { + outputDir = dir; + } + + /** + * Get the directory into which commited class files should be written. + */ + public File outputDir() { + return outputDir; + } + + /** + * Writes a bunch of bytes to an output entry with the given + * name. + */ + public void writeEntry(final byte[] bytes, final String name) + throws IOException { + final OutputStream os = outputStreamFor(name); + os.write(bytes); + os.flush(); + os.close(); + } + + /** + * Returns an OutputStream to which a class file should be + * written. + */ + public OutputStream outputStreamFor(final ClassInfo info) + throws IOException { + // Format the name of the output file + final String name = info.name().replace('/', File.separatorChar) + + ".class"; + return outputStreamFor(name); + } + + /** + * Returns an OutputStream to which somed named entity is + * written. Any forward slashes in the name are replaced by + * File.separatorChar. + */ + protected OutputStream outputStreamFor(String name) throws IOException { + + name = name.replace('/', File.separatorChar); + + final File f = new File(outputDir, name); + + if (f.exists()) { + f.delete(); + } + + final File dir = new File(f.getParent()); + dir.mkdirs(); + + if (!dir.exists()) { + throw new RuntimeException("Couldn't create directory: " + dir); + } + + return (new FileOutputStream(f)); + } + + /** + * Signifies that we are done with this ClassFileLoader + */ + public void done() throws IOException { + // Nothing for this guy + } +} diff --git a/src/EDU/purdue/cs/bloat/file/Code.java b/src/EDU/purdue/cs/bloat/file/Code.java new file mode 100644 index 0000000..83cd929 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Code.java @@ -0,0 +1,442 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Code is used to store the Code attribute of a method in a class file. The + * Code attribute stores the raw bytecode of the method, the maximum stack + * height and maximum number of locals used by the method, and the exception + * handlers used in the method. Code may have several attributes. The local + * variable table and the line number table are modeled explicitly. All other + * attributes are modeled as generic attributes. + * + * @see EDU.purdue.cs.bloat.reflect.Catch Catch + * @see GenericAttribute + * @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo + * @see LocalVariableTable + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Code extends Attribute { + private ClassInfo classInfo; + + private int maxStack; + + private int maxLocals; + + private byte[] code; + + private Catch[] handlers; + + private LineNumberTable lineNumbers; + + private LocalVariableTable locals; + + private Attribute[] attrs; + + /** + * Constructor for creating a Code from scratch + * + * @param codeIndex + * The index in the constant pool for the UTF8 "Code" + */ + Code(final ClassInfo classInfo, final int codeIndex) { + // Don't know length! + super(codeIndex, -1); + + this.classInfo = classInfo; + + // These should get set during a commit + this.maxStack = -1; + this.maxLocals = -1; + this.code = new byte[0]; + this.handlers = new Catch[0]; + this.lineNumbers = null; // It's okay for these to be null + this.locals = null; + this.attrs = new Attribute[0]; + } + + /** + * Constructor. Create a Code attribute from a data stream. + * + * @param in + * The data stream containing the class file. + * @param index + * The index into the constant pool of the name of the attribute. + * @param len + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public Code(final ClassInfo classInfo, final DataInputStream in, + final int index, final int len) throws IOException { + super(index, len); + + this.classInfo = classInfo; + + maxStack = in.readUnsignedShort(); + maxLocals = in.readUnsignedShort(); + + final int codeLength = in.readInt(); + + code = new byte[codeLength]; + + for (int read = 0; read < codeLength;) { + read += in.read(code, read, codeLength - read); + } + + final int numHandlers = in.readUnsignedShort(); + + handlers = new Catch[numHandlers]; + + for (int i = 0; i < numHandlers; i++) { + handlers[i] = readCatch(in); + } + + final int numAttributes = in.readUnsignedShort(); + + attrs = new Attribute[numAttributes]; + + for (int i = 0; i < numAttributes; i++) { + final int nameIndex = in.readUnsignedShort(); + final int length = in.readInt(); + + final Constant name = classInfo.constants()[nameIndex]; + + if (name != null) { + if ("LineNumberTable".equals(name.value())) { + lineNumbers = new LineNumberTable(in, nameIndex, length); + attrs[i] = lineNumbers; + } else if ("LocalVariableTable".equals(name.value())) { + locals = new LocalVariableTable(in, nameIndex, length); + attrs[i] = locals; + } + } + + if (attrs[i] == null) { + attrs[i] = new GenericAttribute(in, nameIndex, length); + } + } + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.writeShort(maxStack); + out.writeShort(maxLocals); + + out.writeInt(code.length); + out.write(code, 0, code.length); + + out.writeShort(handlers.length); + + for (int i = 0; i < handlers.length; i++) { + writeCatch(out, handlers[i]); + } + + out.writeShort(attrs.length); + + for (int i = 0; i < attrs.length; i++) { + out.writeShort(attrs[i].nameIndex()); + out.writeInt(attrs[i].length()); + attrs[i].writeData(out); + } + } + + /** + * Read an exception handler attribute. + * + * @param in + * The data stream of the class file. + * @return A Catch attribute for the handler. + * @exception IOException + * If an error occurs while reading. + */ + private Catch readCatch(final DataInputStream in) throws IOException { + final int startPC = in.readUnsignedShort(); + final int endPC = in.readUnsignedShort(); + final int handlerPC = in.readUnsignedShort(); + final int catchType = in.readUnsignedShort(); + + return new Catch(startPC, endPC, handlerPC, catchType); + } + + /** + * Write an exception handler attribute. + * + * @param out + * The data stream of the class file. + * @param c + * A Catch attribute for the handler. + * @exception IOException + * If an error occurs while writing. + */ + private void writeCatch(final DataOutputStream out, final Catch c) + throws IOException { + final int startPC = c.startPC(); + final int endPC = c.endPC(); + final int handlerPC = c.handlerPC(); + final int catchType = c.catchTypeIndex(); + + out.writeShort(startPC); + out.writeShort(endPC); + out.writeShort(handlerPC); + out.writeShort(catchType); + } + + /** + * Set the maximum height of the operand stack used by the code. + * + * @param maxStack + * The maximum height of the stack. + */ + public void setMaxStack(final int maxStack) { + this.maxStack = maxStack; + } + + /** + * Set the maximum number of locals used by the code. + * + * @param maxLocals + * The maximum number of locals. + */ + public void setMaxLocals(final int maxLocals) { + this.maxLocals = maxLocals; + } + + /** + * Get the maximum height of the operand stack used by the code. + * + * @return The maximum number of locals. + */ + public int maxStack() { + return maxStack; + } + + /** + * Get the maximum number of locals used by the code. + * + * @return The maximum number of locals. + */ + public int maxLocals() { + return maxLocals; + } + + /** + * Set the exception handlers in the method. + * + * @param handlers + * The handlers. + */ + public void setExceptionHandlers(final Catch[] handlers) { + this.handlers = handlers; + } + + /** + * Get the length of the attribute. + * + * @return The length of the attribute. + */ + public int length() { + int length = 2 + 2 + 4 + code.length + 2 + handlers.length * 8 + 2; + + for (int i = 0; i < attrs.length; i++) { + length += 2 + 4 + attrs[i].length(); + } + + return length; + } + + /** + * Get the line number debug info for the code. + * + * @return The line number debug info for the code. + */ + public LineNumberDebugInfo[] lineNumbers() { + if (lineNumbers != null) { + return lineNumbers.lineNumbers(); + } + + return new LineNumberDebugInfo[0]; + } + + /** + * Get the local variable debug info for the code. + * + * @return The local variable debug info for the code. + */ + public LocalDebugInfo[] locals() { + if (locals != null) { + return locals.locals(); + } + + return new LocalDebugInfo[0]; + } + + /** + * Set the line number debug info for the code. + * + * @param lineNumbers + * The line number debug info for the code. + */ + public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { + if (lineNumbers == null) { + for (int i = 0; i < attrs.length; i++) { + if (this.lineNumbers == attrs[i]) { + final Attribute[] a = attrs; + attrs = new Attribute[a.length - 1]; + System.arraycopy(a, 0, attrs, 0, i); + System.arraycopy(a, i + 1, attrs, i, attrs.length - i); + break; + } + } + + this.lineNumbers = null; + } else if (this.lineNumbers != null) { + this.lineNumbers.setLineNumbers(lineNumbers); + } + } + + /** + * Set the local variable debug info for the code. + * + * @param locals + * The local variable debug info for the code. + */ + public void setLocals(final LocalDebugInfo[] locals) { + if (locals == null) { + for (int i = 0; i < attrs.length; i++) { + if (this.locals == attrs[i]) { + final Attribute[] a = attrs; + attrs = new Attribute[a.length - 1]; + System.arraycopy(a, 0, attrs, 0, i); + System.arraycopy(a, i + 1, attrs, i, attrs.length - i); + break; + } + } + + this.locals = null; + } else if (this.locals != null) { + this.locals.setLocals(locals); + } + } + + /** + * Get the exception handlers in the method. + * + * @return The handlers. + */ + public Catch[] exceptionHandlers() { + return handlers; + } + + /** + * Get the bytes of the code. + * + * @return The code. + */ + public byte[] code() { + return code; + } + + /** + * Return the length of the code array + */ + public int codeLength() { + return (code.length); + } + + /** + * Set the bytes of the code. + * + * @param code + * The code. + */ + public void setCode(final byte[] code) { + this.code = code; + } + + /** + * Private constructor for cloning. + */ + private Code(final Code other) { + super(other.nameIndex, other.length); + + this.classInfo = other.classInfo; + this.maxStack = other.maxStack; + this.maxLocals = other.maxLocals; + + this.code = new byte[other.code.length]; + System.arraycopy(other.code, 0, this.code, 0, other.code.length); + this.handlers = new Catch[other.handlers.length]; + for (int i = 0; i < other.handlers.length; i++) { + this.handlers[i] = (Catch) other.handlers[i].clone(); + } + + if (other.lineNumbers != null) { + this.lineNumbers = (LineNumberTable) other.lineNumbers.clone(); + } + + if (other.locals != null) { + this.locals = (LocalVariableTable) other.locals.clone(); + } + + this.attrs = new Attribute[other.attrs.length]; + for (int i = 0; i < other.attrs.length; i++) { + this.attrs[i] = other.attrs[i]; + } + } + + public Object clone() { + return (new Code(this)); + } + + /** + * Returns a string representation of the attribute. + */ + public String toString() { + String x = ""; + + if (handlers != null) { + for (int i = 0; i < handlers.length; i++) { + x += "\n " + handlers[i]; + } + } + + /* + * for (int i = 0; i < attrs.length; i++) { x += "\n " + attrs[i]; } + */ + + return "(code " + maxStack + " " + maxLocals + " " + code.length + x + + ")"; + } +} diff --git a/src/EDU/purdue/cs/bloat/file/ConstantValue.java b/src/EDU/purdue/cs/bloat/file/ConstantValue.java new file mode 100644 index 0000000..65583b5 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/ConstantValue.java @@ -0,0 +1,115 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +/** + * The ConstantValue attribute stores an index into the constant pool that + * represents constant value. A class's static fields have constant value + * attributes. + * + * @see Field + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class ConstantValue extends Attribute { + private int constantValueIndex; + + /** + * Creates a new ConstantValue from scratch + * + * @param nameIndex + * The index in the constant pool of the UTF8 string + * "ConstantValue" + * @param constantValueIndex + * The index in the constant pool of the Constant containing the + * constant value + */ + ConstantValue(final int nameIndex, final int length, + final int constantValueIndex) { + super(nameIndex, length); + this.constantValueIndex = constantValueIndex; + } + + /** + * Constructor. Create a ConstantValue attribute from a data stream. + * + * @param in + * The data stream of the class file. + * @param nameIndex + * The index into the constant pool of the name of the attribute. + * @param length + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public ConstantValue(final DataInputStream in, final int nameIndex, + final int length) throws IOException { + super(nameIndex, length); + constantValueIndex = in.readUnsignedShort(); + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.writeShort(constantValueIndex); + } + + /** + * Returns the index into the constant pool of the constant value. + */ + public int constantValueIndex() { + return constantValueIndex; + } + + /** + * Set the index into the constant pool of the constant value. + */ + public void setConstantValueIndex(final int index) { + this.constantValueIndex = index; + } + + /** + * Private constructor used for cloning. + */ + private ConstantValue(final ConstantValue other) { + super(other.nameIndex, other.length); + + this.constantValueIndex = other.constantValueIndex; + } + + public Object clone() { + return (new ConstantValue(this)); + } + + /** + * Converts the attribute to a string. + */ + public String toString() { + return "(constant-value " + constantValueIndex + ")"; + } +} diff --git a/src/EDU/purdue/cs/bloat/file/Exceptions.java b/src/EDU/purdue/cs/bloat/file/Exceptions.java new file mode 100644 index 0000000..b7b991f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Exceptions.java @@ -0,0 +1,140 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Exceptions describes the types of exceptions that a method may throw. The + * Exceptions attribute stores a list of indices into the constant pool of the + * typs of exceptions thrown by the method. + * + * @see Method + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Exceptions extends Attribute { + private int[] exceptions; + + private ClassInfo classInfo; + + /** + * Constructor for create an Exceptions from scratch. + * + * @param nameIndex + * The index of the UTF8 string "Exceptions" in the class's + * constant pool + * @param exceptions + * A non-null array of indices into the constant + * pool for the types of the exceptions + */ + Exceptions(final ClassInfo info, final int nameIndex, final int[] exceptions) { + super(nameIndex, (2 * exceptions.length) + 2); + this.classInfo = info; + this.exceptions = exceptions; + } + + /** + * Constructor. Create an Exceptions attribute from a data stream. + * + * @param in + * The data stream of the class file. + * @param nameIndex + * The index into the constant pool of the name of the attribute. + * @param length + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public Exceptions(final ClassInfo classInfo, final DataInputStream in, + final int nameIndex, final int length) throws IOException { + super(nameIndex, length); + + this.classInfo = classInfo; + + final int count = in.readUnsignedShort(); + + exceptions = new int[count]; + + for (int i = 0; i < count; i++) { + exceptions[i] = in.readUnsignedShort(); + } + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.writeShort(exceptions.length); + + for (int i = 0; i < exceptions.length; i++) { + out.writeShort(exceptions[i]); + } + } + + /** + * Get the indices into the constant pool of the types of the exceptions + * thrown by this method. + * + * @return The indices of the types of the exceptions thrown. + */ + public int[] exceptionTypes() { + return exceptions; + } + + /** + * Get the length of the attribute. + */ + public int length() { + return 2 + exceptions.length * 2; + } + + /** + * Private constructor used for cloning. + */ + private Exceptions(final Exceptions other) { + super(other.nameIndex, other.length); + + this.exceptions = new int[other.exceptions.length]; + System.arraycopy(other.exceptions, 0, this.exceptions, 0, + other.exceptions.length); + this.classInfo = other.classInfo; + } + + public Object clone() { + return (new Exceptions(this)); + } + + /** + * Returns a string representation of the attribute. + */ + public String toString() { + return "(exceptions)"; + } +} diff --git a/src/EDU/purdue/cs/bloat/file/Field.java b/src/EDU/purdue/cs/bloat/file/Field.java new file mode 100644 index 0000000..128f126 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Field.java @@ -0,0 +1,293 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Field models a field (member variable) in a class. The Field class grants + * access to information such as the field's modifiers, its name and type + * descriptor (represented as indices into the constant pool), and any + * attributes of the field. Static fields have a ConstantValue attribute. + * + * @see ConstantValue + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Field implements FieldInfo { + private ClassInfo classInfo; + + private int modifiers; + + private int name; + + private int type; + + private Attribute[] attrs; + + private ConstantValue constantValue; + + /** + * Constructor for creating a new field from scratch + */ + Field(final ClassInfo classInfo, final int modifiers, final int typeIndex, + final int nameIndex) { + this.classInfo = classInfo; + this.modifiers = modifiers; + this.name = nameIndex; + this.type = typeIndex; + this.attrs = new Attribute[0]; + this.constantValue = null; + } + + /** + * Constructor for creating a new field that has a constant value from + * scratch + */ + Field(final ClassInfo classInfo, final int modifiers, final int typeIndex, + final int nameIndex, final int cvNameIndex, + final int constantValueIndex) { + this.classInfo = classInfo; + this.modifiers = modifiers; + this.name = nameIndex; + this.type = typeIndex; + this.constantValue = new ConstantValue(cvNameIndex, 2, + constantValueIndex); + + // The constant value is an attribute + this.attrs = new Attribute[1]; + this.attrs[0] = constantValue; + } + + /** + * Constructor. Read a field from a class file. + * + * @param in + * The data stream of the class file. + * @param classInfo + * The class file containing the field. + * @exception IOException + * If an error occurs while reading. + */ + public Field(final DataInputStream in, final ClassInfo classInfo) + throws IOException { + this.classInfo = classInfo; + + modifiers = in.readUnsignedShort(); + + name = in.readUnsignedShort(); + type = in.readUnsignedShort(); + + final int numAttributes = in.readUnsignedShort(); + + attrs = new Attribute[numAttributes]; + + for (int i = 0; i < numAttributes; i++) { + final int nameIndex = in.readUnsignedShort(); + final int length = in.readInt(); + + final Constant name = classInfo.constants()[nameIndex]; + + if (name != null) { + if ("ConstantValue".equals(name.value())) { + constantValue = new ConstantValue(in, nameIndex, length); + attrs[i] = constantValue; + } + } + + if (attrs[i] == null) { + attrs[i] = new GenericAttribute(in, nameIndex, length); + } + } + } + + /** + * Get the class which declared the field. + * + * @return The ClassInfo of the class which declared the field. + */ + public ClassInfo declaringClass() { + return classInfo; + } + + /** + * Set the index into the constant pool of the name of the field. + * + * @param name + * The name of the field. + */ + public void setNameIndex(final int name) { + this.name = name; + } + + /** + * Set the index into the constant pool of the type of the field. + * + * @param type + * The type of the field. + */ + public void setTypeIndex(final int type) { + this.type = type; + } + + /** + * Get the index into the constant pool of the name of the field. + * + * @return The index into the constant pool of the name of the field. + */ + public int nameIndex() { + return name; + } + + /** + * Get the index into the constant pool of the type of the field. + * + * @return The index into the constant pool of the type of the field. + */ + public int typeIndex() { + return type; + } + + /** + * Set the modifiers of the field. The values correspond to the constants in + * the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the field. + * @see Modifiers + */ + public void setModifiers(final int modifiers) { + this.modifiers = modifiers; + } + + /** + * Get the modifiers of the field. The values correspond to the constants in + * the Modifiers class. + * + * @return A bit vector of modifier flags for the field. + * @see Modifiers + */ + public int modifiers() { + return modifiers; + } + + /** + * Get the index into the constant pool of the field's constant value, if + * any. Returns 0 if the field does not have a constant value. + * + * @see ClassInfo#constants + */ + public int constantValue() { + if (constantValue != null) { + return constantValue.constantValueIndex(); + } + return 0; + } + + /** + * Set the index into the constant pool of the field's constant value. + * + * @see ClassInfo#constants + */ + public void setConstantValue(final int index) { + if (constantValue != null) { + constantValue.setConstantValueIndex(index); + } + } + + /** + * Write the field to a class file. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void write(final DataOutputStream out) throws IOException { + out.writeShort(modifiers); + + out.writeShort(name); + out.writeShort(type); + + out.writeShort(attrs.length); + + for (int i = 0; i < attrs.length; i++) { + out.writeShort(attrs[i].nameIndex()); + out.writeInt(attrs[i].length()); + attrs[i].writeData(out); + } + } + + /** + * Convert the field to a string. + * + * @return A string representation of the field. + */ + public String toString() { + String x = ""; + + x += " (modifiers"; + + if ((modifiers & Modifiers.PUBLIC) != 0) { + x += " PUBLIC"; + } + if ((modifiers & Modifiers.PRIVATE) != 0) { + x += " PRIVATE"; + } + if ((modifiers & Modifiers.PROTECTED) != 0) { + x += " PROTECTED"; + } + if ((modifiers & Modifiers.STATIC) != 0) { + x += " STATIC"; + } + if ((modifiers & Modifiers.FINAL) != 0) { + x += " FINAL"; + } + if ((modifiers & Modifiers.SYNCHRONIZED) != 0) { + x += " SYNCHRONIZED"; + } + if ((modifiers & Modifiers.VOLATILE) != 0) { + x += " VOLATILE"; + } + if ((modifiers & Modifiers.TRANSIENT) != 0) { + x += " TRANSIENT"; + } + if ((modifiers & Modifiers.NATIVE) != 0) { + x += " NATIVE"; + } + if ((modifiers & Modifiers.INTERFACE) != 0) { + x += " INTERFACE"; + } + if ((modifiers & Modifiers.ABSTRACT) != 0) { + x += " ABSTRACT"; + } + x += ")"; + + if (constantValue != null) { + x += " " + constantValue; + } + + return "(field " + name + " " + type + x + ")"; + } +} diff --git a/src/EDU/purdue/cs/bloat/file/GenericAttribute.java b/src/EDU/purdue/cs/bloat/file/GenericAttribute.java new file mode 100644 index 0000000..4ab52a0 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/GenericAttribute.java @@ -0,0 +1,82 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +/** + * The Java Virtual Machine Specification allows implementors to invent their + * own attributes. GenericAttribute models attributes whose name BLOAT does not + * recognize. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class GenericAttribute extends Attribute { + private byte[] data; + + /** + * Constructor. Create an attribute from a data stream. + * + * @param in + * The data stream of the class file. + * @param nameIndex + * The index into the constant pool of the name of the attribute. + * @param length + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public GenericAttribute(final DataInputStream in, final int nameIndex, + final int length) throws IOException { + super(nameIndex, length); + data = new byte[length]; + for (int read = 0; read < length;) { + read += in.read(data, read, length - read); + } + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.write(data, 0, data.length); + } + + /** + * Private constructor used in cloning. + */ + private GenericAttribute(final GenericAttribute other) { + super(other.nameIndex, other.length); + + this.data = new byte[other.data.length]; + System.arraycopy(other.data, 0, this.data, 0, other.data.length); + } + + public Object clone() { + return (new GenericAttribute(this)); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/JarFileCommitter.java b/src/EDU/purdue/cs/bloat/file/JarFileCommitter.java new file mode 100644 index 0000000..821628f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/JarFileCommitter.java @@ -0,0 +1,260 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.zip.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * Does a lot of the same stuff as ClassFileLoader, but classes are + * committed to a JAR file instead of regular files. + */ +public class JarFileCommitter extends ClassFileLoader { + + private FunkyJar funky; + + /** + * Constructor. + * + * @param file + * File representing JAR file + * @param compress + * If true, contents of JAR file is compressed + * @param version + * Version for the JAR file's manifest + * @param author + * Author string from JAR file's manifest + */ + public JarFileCommitter(final File file, final boolean compress, + final String version, final String author) throws IOException { + + funky = new FunkyJar(file, compress, version, author); + } + + protected OutputStream outputStreamFor(final String name) + throws IOException { + + funky.newEntry(name); + return funky; + } + + public OutputStream outputStreamFor(final ClassInfo info) + throws IOException { + // This is funky. Recall that a JarOutputStream is also an output + // stream. So, we just return it. This is why we have to + // override the write, etc. methods. + + // Make a new entry based on the class name + final String name = info.name() + ".class"; + return outputStreamFor(name); + } + + /** + * Signifies that we are finished with this JarFileCommitter. + */ + public void done() throws IOException { + funky.done(); + } +} + +/** + * We subclass JarOutputStream so that we can return an OutputStream to which a + * BLOATed class file will be written. In order to accomodate non-compression, + * we have to perform the checksum along the way. Bletch. + */ +class FunkyJar extends JarOutputStream { + + private static final String MANIFEST = JarFile.MANIFEST_NAME; + + private static final String MANIFEST_DIR = "META-INF/"; + + private static final CRC32 crc32 = new CRC32(); + + private boolean compress; + + private JarEntry currEntry; + + private Size size; + + class Size { + long value = 0; + } + + /** + * Constructor. + */ + public FunkyJar(final File file, boolean compress, final String version, + final String author) throws IOException { + super(new FileOutputStream(file)); + + this.compress = compress; + + if (compress) { + this.setMethod(ZipOutputStream.DEFLATED); + } else { + this.setMethod(ZipOutputStream.STORED); + } + + final Manifest manifest = new Manifest(); + final Attributes global = manifest.getMainAttributes(); + if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) { + global.put(Attributes.Name.MANIFEST_VERSION, version); + } + + if (global.getValue(new Attributes.Name("Created-By")) == null) { + global.put(new Attributes.Name("Created-By"), author); + } + + // Add directory for manifest + JarEntry entry = new JarEntry(FunkyJar.MANIFEST_DIR); + entry.setTime(System.currentTimeMillis()); + entry.setSize(0); // Directories have size 0 + entry.setCrc(0); // Checksum is 0 + this.putNextEntry(entry); + + // Add manifest + entry = new JarEntry(FunkyJar.MANIFEST); + entry.setTime(System.currentTimeMillis()); + if (!compress) { + // Have to compute checksum ourselves. Use an ugly anonymous + // inner class. Influenced by CRC32OutputStream in + // sun.tools.jar.Main. Please don't sue me. I have no money. + // Maybe you could give me a job instead. Of course, then I'd + // have money and you would sue me. Hmm. + final Size size = new Size(); + FunkyJar.crc32.reset(); + manifest.write(new OutputStream() { + public void write(final int r) throws IOException { + FunkyJar.crc32.update(r); + size.value++; + } + + public void write(final byte[] b) throws IOException { + FunkyJar.crc32.update(b, 0, b.length); + size.value += b.length; + } + + public void write(final byte[] b, final int off, final int len) + throws IOException { + FunkyJar.crc32.update(b, off, len); + size.value += len - off; + } + }); + entry.setSize(size.value); + entry.setCrc(FunkyJar.crc32.getValue()); + } + this.putNextEntry(entry); + manifest.write(this); // Write the manifest to JAR file + this.closeEntry(); + } + + public void newEntry(final String name) throws IOException { + makeDirs(name); + + currEntry = new JarEntry(name); + currEntry.setTime(System.currentTimeMillis()); + if (compress) { + currEntry.setMethod(ZipEntry.DEFLATED); + } else { + currEntry.setMethod(ZipEntry.STORED); + } + this.putNextEntry(currEntry); + FunkyJar.crc32.reset(); + this.size = new Size(); + } + + private Set dirs; + + /** + * look at the path name specified by key and create zip entries for each + * directory level not already added. + */ + private void makeDirs(final String key) throws IOException { + if (dirs == null) { + dirs = new HashSet(); + } + int idx = 0; + int last = 0; + while ((last = key.indexOf('/', idx + 1)) != -1) { + final String aDir = key.substring(0, last + 1); + if (!dirs.contains(aDir)) { + dirs.add(aDir); + this.putNextEntry(new ZipEntry(aDir)); + this.closeEntry(); + } + idx = last; + } + } + + public void write(final int r) throws IOException { + super.write(r); + + if (!compress && (size != null)) { + FunkyJar.crc32.update(r); + size.value++; + } + } + + public void write(final byte[] b) throws IOException { + super.write(b); + + if (!compress && (size != null)) { + FunkyJar.crc32.update(b, 0, b.length); + size.value += b.length; + } + } + + public void write(final byte[] b, final int off, final int len) + throws IOException { + super.write(b, off, len); + + if (!compress && (size != null)) { + FunkyJar.crc32.update(b, off, len); + size.value += len - off; + } + } + + public void close() throws IOException { + // Okay, everythings is done. Set some values for the entry, + // cross your fingers, and run away. + if (!compress && (size != null)) { + currEntry.setSize(size.value); + currEntry.setCrc(FunkyJar.crc32.getValue()); + } + + currEntry = null; + size = null; + this.closeEntry(); + + // Note that we don't invoke the super class method. + } + + /** + * Signifies that we are finished with this JarFileCommitter. + */ + public void done() throws IOException { + super.close(); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/LineNumberTable.java b/src/EDU/purdue/cs/bloat/file/LineNumberTable.java new file mode 100644 index 0000000..66782d2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/LineNumberTable.java @@ -0,0 +1,142 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * LineNumberTable is an attribute of a code attribute. A LineNumberTable stores + * information that relates indices into the code array (instructions) to the + * lines of code in the source file from which they were compiled. This optional + * attribute is used with debuggers (duh) and consists of an array of + * reflect.LineNumberDebugInfo. + * + * @see Code + * @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class LineNumberTable extends Attribute { + private LineNumberDebugInfo[] lineNumbers; + + /** + * Constructor. Create an attribute from a data stream. + * + * @param in + * The data stream of the class file. + * @param nameIndex + * The index into the constant pool of the name of the attribute. + * @param length + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public LineNumberTable(final DataInputStream in, final int nameIndex, + final int length) throws IOException { + super(nameIndex, length); + + final int numLines = in.readUnsignedShort(); + + lineNumbers = new LineNumberDebugInfo[numLines]; + + for (int i = 0; i < lineNumbers.length; i++) { + final int startPC = in.readUnsignedShort(); + final int lineNumber = in.readUnsignedShort(); + lineNumbers[i] = new LineNumberDebugInfo(startPC, lineNumber); + } + } + + /** + * Get the line number debug info for the code. + * + * @return The line number debug info for the code. + */ + public LineNumberDebugInfo[] lineNumbers() { + return lineNumbers; + } + + /** + * Set the line number debug info for the code. + * + * @param lineNumbers + * The line number debug info for the code. + */ + public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { + this.lineNumbers = lineNumbers; + } + + /** + * Get the length of the attribute. + * + * @return The length of the attribute. + */ + public int length() { + return 2 + lineNumbers.length * 4; + } + + public String toString() { + String x = "(lines"; + + for (int i = 0; i < lineNumbers.length; i++) { + x += "\n (line #" + lineNumbers[i].lineNumber() + " pc=" + + lineNumbers[i].startPC() + ")"; + } + + return x + ")"; + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.writeShort(lineNumbers.length); + + for (int i = 0; i < lineNumbers.length; i++) { + out.writeShort(lineNumbers[i].startPC()); + out.writeShort(lineNumbers[i].lineNumber()); + } + } + + /** + * Private constructor used in cloning. + */ + private LineNumberTable(final LineNumberTable other) { + super(other.nameIndex, other.length); + + this.lineNumbers = new LineNumberDebugInfo[other.lineNumbers.length]; + for (int i = 0; i < other.lineNumbers.length; i++) { + this.lineNumbers[i] = (LineNumberDebugInfo) other.lineNumbers[i] + .clone(); + } + } + + public Object clone() { + return (new LineNumberTable(this)); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/LocalVariableTable.java b/src/EDU/purdue/cs/bloat/file/LocalVariableTable.java new file mode 100644 index 0000000..dbdbd04 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/LocalVariableTable.java @@ -0,0 +1,147 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; + +/** + * LocalVariableTable represents debugging information that may be used by a + * debugger to determine the value of a given local variable during program + * execution. It is essentially an array of reflect.LocalDebugInfo. + * + * @see EDU.purdue.cs.bloat.reflect.LocalDebugInfo + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class LocalVariableTable extends Attribute { + private LocalDebugInfo[] locals; + + /** + * Constructor. Create an attribute from a data stream. + * + * @param in + * The data stream of the class file. + * @param index + * The index into the constant pool of the name of the attribute. + * @param len + * The length of the attribute, excluding the header. + * @exception IOException + * If an error occurs while reading. + */ + public LocalVariableTable(final DataInputStream in, final int index, + final int len) throws IOException { + super(index, len); + + final int numLocals = in.readUnsignedShort(); + + locals = new LocalDebugInfo[numLocals]; + + for (int i = 0; i < locals.length; i++) { + final int startPC = in.readUnsignedShort(); + final int length = in.readUnsignedShort(); + final int nameIndex = in.readUnsignedShort(); + final int typeIndex = in.readUnsignedShort(); + final int varIndex = in.readUnsignedShort(); + locals[i] = new LocalDebugInfo(startPC, length, nameIndex, + typeIndex, varIndex); + } + } + + /** + * Get the local variable debug info for the code. + * + * @return The local variable debug info for the code. + */ + public LocalDebugInfo[] locals() { + return locals; + } + + /** + * Set the local variable debug info for the code. + * + * @param locals + * The local variable debug info for the code. + */ + public void setLocals(final LocalDebugInfo[] locals) { + this.locals = locals; + } + + /** + * Get the length of the attribute. + * + * @return The length of the attribute. + */ + public int length() { + return 2 + locals.length * 10; + } + + public String toString() { + String x = "(locals"; + + for (int i = 0; i < locals.length; i++) { + x += "\n (local @" + locals[i].index() + " name=" + + locals[i].nameIndex() + " type=" + locals[i].typeIndex() + + " pc=" + locals[i].startPC() + ".." + + (locals[i].startPC() + locals[i].length()) + ")"; + } + + return x + ")"; + } + + /** + * Write the attribute to a data stream. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void writeData(final DataOutputStream out) throws IOException { + out.writeShort(locals.length); + + for (int i = 0; i < locals.length; i++) { + out.writeShort(locals[i].startPC()); + out.writeShort(locals[i].length()); + out.writeShort(locals[i].nameIndex()); + out.writeShort(locals[i].typeIndex()); + out.writeShort(locals[i].index()); + } + } + + /** + * Private constructor used in cloning. + */ + private LocalVariableTable(final LocalVariableTable other) { + super(other.nameIndex, other.length); + + this.locals = new LocalDebugInfo[other.locals.length]; + for (int i = 0; i < other.locals.length; i++) { + this.locals[i] = (LocalDebugInfo) other.locals[i].clone(); + } + } + + public Object clone() { + return (new LocalVariableTable(this)); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/Makefile b/src/EDU/purdue/cs/bloat/file/Makefile new file mode 100644 index 0000000..b020533 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Makefile @@ -0,0 +1,33 @@ +# 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. +# +# 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 + +CLASS = \ + Attribute.class\ + ClassFile.class\ + ClassFileLoader.class\ + Code.class\ + ConstantValue.class\ + Exceptions.class\ + Field.class\ + GenericAttribute.class\ + JarFileCommitter.class\ + LineNumberTable.class\ + LocalVariableTable.class\ + Method.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/file/Method.java b/src/EDU/purdue/cs/bloat/file/Method.java new file mode 100644 index 0000000..09385fe --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/Method.java @@ -0,0 +1,484 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.file; + +import java.io.*; + +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Method represents a method in a Java classfile. A method's name and value + * (the types of its parameters and its return type) are modeled as indices into + * it class's constant pool. A method has modifiers that determine whether it is + * public, private, static, final, etc. Methods have a number of attributes such + * as their Code and any Exceptions they may throw. + * + * @see Code + * @see Exceptions + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Method implements MethodInfo { + private ClassInfo classInfo; + + private int modifiers; + + private int name; + + private int type; + + private Attribute[] attrs; + + private Code code; + + private Exceptions exceptions; + + public static boolean DEBUG = Boolean.getBoolean("Method.DEBUG"); + + /** + * Constructor for creating a Method from scratch + * + * @param attrs + * Must include code and exceptions + * which are themselves attributes of this method + */ + Method(final ClassInfo classInfo, final int modifiers, final int name, + final int type, final Attribute[] attrs, final Code code, + final Exceptions exceptions) { + this.classInfo = classInfo; + this.modifiers = modifiers; + this.name = name; + this.type = type; + + Assert.isNotNull(attrs, "Every method must have at least a Code " + + "attribute"); + this.attrs = attrs; + this.code = code; + this.exceptions = exceptions; + } + + /** + * Constructor. Read a method from a class file. + * + * @param in + * The data stream of the class file. + * @param classInfo + * The class file containing the method. + * @exception IOException + * If an error occurs while reading. + */ + public Method(final DataInputStream in, final ClassInfo classInfo) + throws IOException { + this.classInfo = classInfo; + + modifiers = in.readUnsignedShort(); + + name = in.readUnsignedShort(); + type = in.readUnsignedShort(); + + final int numAttributes = in.readUnsignedShort(); + + attrs = new Attribute[numAttributes]; + + for (int i = 0; i < numAttributes; i++) { + final int nameIndex = in.readUnsignedShort(); + final int length = in.readInt(); + + final Constant name = classInfo.constants()[nameIndex]; + + if (name != null) { + if ("Code".equals(name.value())) { + code = new Code(classInfo, in, nameIndex, length); + attrs[i] = code; + } else if ("Exceptions".equals(name.value())) { + exceptions = new Exceptions(classInfo, in, nameIndex, + length); + attrs[i] = exceptions; + } + } + + if (attrs[i] == null) { + attrs[i] = new GenericAttribute(in, nameIndex, length); + } + } + } + + /** + * Get the class which declared the method. + * + * @return The ClassInfo of the class which declared the method. + */ + public ClassInfo declaringClass() { + return classInfo; + } + + /** + * Set the index into the constant pool of the name of the method. + * + * @param name + * The index into the constant pool of the name of the method. + */ + public void setNameIndex(final int name) { + this.name = name; + } + + /** + * Set the index into the constant pool of the type of the method. + * + * @param type + * The index into the constant pool of the type of the method. + */ + public void setTypeIndex(final int type) { + this.type = type; + } + + /** + * Get the index into the constant pool of the name of the method. + * + * @return The index into the constant pool of the name of the method. + */ + public int nameIndex() { + return name; + } + + /** + * Get the index into the constant pool of the type of the method. + * + * @return The index into the constant pool of the type of the method. + */ + public int typeIndex() { + return type; + } + + /** + * Set the modifiers of the method. The values correspond to the constants + * in the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the method. + * @see Modifiers + */ + public void setModifiers(final int modifiers) { + this.modifiers = modifiers; + } + + /** + * Get the modifiers of the method. The values correspond to the constants + * in the Modifiers class. + * + * @return A bit vector of modifier flags for the method. + * @see Modifiers + */ + public int modifiers() { + return modifiers; + } + + /** + * Get the maximum height of the operand stack. + * + * @return The maximum height of the operand stack. + */ + public int maxStack() { + if (code != null) { + return code.maxStack(); + } + return 0; + } + + /** + * Set the maximum height of the operand stack. + * + * @param maxStack + * The maximum height of the operand stack. + */ + public void setMaxStack(final int maxStack) { + if (code != null) { + code.setMaxStack(maxStack); + } + } + + /** + * Get the maximum number of locals used in the method. + * + * @return The maximum number of locals used in the method. + */ + public int maxLocals() { + if (code != null) { + return code.maxLocals(); + } + return 0; + } + + /** + * Set the maximum number of locals used in the method. + * + * @param maxLocals + * The maximum number of locals used in the method. + */ + public void setMaxLocals(final int maxLocals) { + if (code != null) { + code.setMaxLocals(maxLocals); + } + } + + /** + * Get the byte code array of the method. + * + * @return The byte code array of the method. + */ + public byte[] code() { + if (code != null) { + return code.code(); + } + return new byte[0]; + } + + /** + * Returns the length of the Code array. + */ + public int codeLength() { + if (code != null) { + return (code.codeLength()); + } else { + return (0); + } + } + + /** + * Set the byte code array of the method. + * + * @param bytes + * The byte code array of the method. + */ + public void setCode(final byte[] bytes) { + if (code != null) { + code.setCode(bytes); + if (Method.DEBUG) { + System.out.println("Set code with " + bytes.length + " bytes"); + // Thread.dumpStack(); + } + } + } + + /** + * Get the indices into the constant pool of the types of the exceptions + * thrown by the method. + * + * @return The indices into the constant pool of the types of the exceptions + * thrown by the method. + */ + public int[] exceptionTypes() { + if (exceptions != null) { + return exceptions.exceptionTypes(); + } + return new int[0]; + } + + /** + * Set the line numbers of the instructions in the method. + * + * @param lineNumbers + * The line numbers of the instructions in the method. + */ + public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) { + if (code != null) { + code.setLineNumbers(lineNumbers); + } + } + + /** + * Set the local variable debug info for the method. + * + * @param locals + * The local variable debug info for the method. + */ + public void setLocals(final LocalDebugInfo[] locals) { + if (code != null) { + code.setLocals(locals); + } + } + + /** + * Get the line numbers of the instructions in the method. + * + * @return The line numbers of the instructions in the method. + */ + public LineNumberDebugInfo[] lineNumbers() { + if (code != null) { + return code.lineNumbers(); + } + return new LineNumberDebugInfo[0]; + } + + /** + * Get the local variable debug info for the method. + * + * @return The local variable debug info for the method. + */ + public LocalDebugInfo[] locals() { + if (code != null) { + return code.locals(); + } + return new LocalDebugInfo[0]; + } + + /** + * Get the exception handlers in the method. + * + * @return The exception handlers in the method. + */ + public Catch[] exceptionHandlers() { + if (code != null) { + return code.exceptionHandlers(); + } + return new Catch[0]; + } + + /** + * Set the exception handlers in the method. + * + * @param handlers + * The exception handlers in the method. + */ + public void setExceptionHandlers(final Catch[] handlers) { + if (code != null) { + code.setExceptionHandlers(handlers); + } + } + + /** + * Write the method to a class file. + * + * @param out + * The data stream of the class file. + * @exception IOException + * If an error occurs while writing. + */ + public void write(final DataOutputStream out) throws IOException { + if (Method.DEBUG) { + System.out.println("Writing method " + this); + System.out.println(" Masked Modifiers: " + (modifiers & 0xf000)); + } + + out.writeShort(modifiers); + + out.writeShort(name); + out.writeShort(type); + + out.writeShort(attrs.length); + + for (int i = 0; i < attrs.length; i++) { + if (Method.DEBUG) { + System.out.println(" " + attrs[i]); + } + out.writeShort(attrs[i].nameIndex()); + out.writeInt(attrs[i].length()); + attrs[i].writeData(out); + } + } + + /** + * Returns a string representation of the method. + */ + public String toString() { + String x = ""; + + x += " (modifiers"; + + if ((modifiers & Modifiers.PUBLIC) != 0) { + x += " PUBLIC"; + } + if ((modifiers & Modifiers.PRIVATE) != 0) { + x += " PRIVATE"; + } + if ((modifiers & Modifiers.PROTECTED) != 0) { + x += " PROTECTED"; + } + if ((modifiers & Modifiers.STATIC) != 0) { + x += " STATIC"; + } + if ((modifiers & Modifiers.FINAL) != 0) { + x += " FINAL"; + } + if ((modifiers & Modifiers.SYNCHRONIZED) != 0) { + x += " SYNCHRONIZED"; + } + if ((modifiers & Modifiers.VOLATILE) != 0) { + x += " VOLATILE"; + } + if ((modifiers & Modifiers.TRANSIENT) != 0) { + x += " TRANSIENT"; + } + if ((modifiers & Modifiers.NATIVE) != 0) { + x += " NATIVE"; + } + if ((modifiers & Modifiers.INTERFACE) != 0) { + x += " INTERFACE"; + } + if ((modifiers & Modifiers.ABSTRACT) != 0) { + x += " ABSTRACT"; + } + x += ")"; + + return "(method " + name + " " + type + x + + (code != null ? "\n " + code : "") + + (exceptions != null ? "\n " + exceptions : "") + ")"; + } + + /** + * Constructor used for cloning a Method + */ + private Method(final ClassInfo classInfo, final int modifiers, + final int name, final int type, final Attribute[] attrs) { + this.classInfo = classInfo; + this.modifiers = modifiers; + this.name = name; + this.type = type; + + if (attrs != null) { + this.attrs = new Attribute[attrs.length]; + for (int i = 0; i < attrs.length; i++) { + final Attribute attr = (Attribute) attrs[i].clone(); + if (attr instanceof Code) { + this.code = (Code) attr; + + } else if (attr instanceof Exceptions) { + this.exceptions = (Exceptions) attr; + } + this.attrs[i] = attr; + } + } + + Assert.isTrue(code != null, "No Code in attributes"); + Assert.isTrue(exceptions != null, "No Exceptions in attributes"); + } + + /** + * Creates a clone of this MethodInfo except that its declaring + * class does not know about it. + */ + public Object clone() { + return (new Method(this.classInfo, this.modifiers, this.name, + this.type, this.attrs)); + } +} diff --git a/src/EDU/purdue/cs/bloat/file/package.html b/src/EDU/purdue/cs/bloat/file/package.html new file mode 100644 index 0000000..ff98eb4 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/file/package.html @@ -0,0 +1,11 @@ + + + +

Allows access to Java classes stored in files on disk. Many of +these classes implement the interfaces found in the +EDU.purdue.cs.bloat.reflect package. Classes are loaded from a file +and things such as constant values, methods, code, debugging +information, and exceptions are modeled.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/inline/.cvsignore b/src/EDU/purdue/cs/bloat/inline/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..06c6d8f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/CallGraph.java @@ -0,0 +1,763 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.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(final 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. + + CallGraph.preLive = new HashSet(); + + CallGraph.preLive.add("java.lang.Boolean"); + CallGraph.preLive.add("java.lang.Class"); + CallGraph.preLive.add("java.lang.ClassLoader"); + CallGraph.preLive.add("java.lang.Compiler"); + CallGraph.preLive.add("java.lang.Integer"); + CallGraph.preLive.add("java.lang.SecurityManager"); + CallGraph.preLive.add("java.lang.String"); + CallGraph.preLive.add("java.lang.StringBuffer"); + CallGraph.preLive.add("java.lang.System"); + CallGraph.preLive.add("java.lang.StackOverflowError"); + CallGraph.preLive.add("java.lang.Thread"); + CallGraph.preLive.add("java.lang.ThreadGroup"); + + CallGraph.preLive.add("java.io.BufferedInputStream"); + CallGraph.preLive.add("java.io.BufferedReader"); + CallGraph.preLive.add("java.io.BufferedOutputStream"); + CallGraph.preLive.add("java.io.BufferedWriter"); + CallGraph.preLive.add("java.io.File"); + CallGraph.preLive.add("java.io.FileDescriptor"); + CallGraph.preLive.add("java.io.InputStreamReader"); + CallGraph.preLive.add("java.io.ObjectStreamClass"); + CallGraph.preLive.add("java.io.OutputStreamWriter"); + CallGraph.preLive.add("java.io.PrintStream"); + CallGraph.preLive.add("java.io.PrintWriter"); + + CallGraph.preLive.add("java.net.URL"); + + CallGraph.preLive.add("java.security.Provider"); + CallGraph.preLive.add("java.security.Security"); + + CallGraph.preLive.add("java.util.Hashtable"); + CallGraph.preLive.add("java.util.ListResourceBundle"); + CallGraph.preLive.add("java.util.Locale"); + CallGraph.preLive.add("java.util.Properties"); + CallGraph.preLive.add("java.util.Stack"); + CallGraph.preLive.add("java.util.Vector"); + + CallGraph.preLive.add("java.util.zip.ZipFile"); + + // Some pre-live classes are only available on JDK1.2. + if (CallGraph.USE1_2) { + CallGraph.preLive.add("java.lang.Package"); + + CallGraph.preLive.add("java.lang.ref.Finalizer"); + CallGraph.preLive.add("java.lang.ref.ReferenceQueue"); + + CallGraph.preLive.add("java.io.FilePermission"); + CallGraph.preLive.add("java.io.UnixFileSystem"); + + CallGraph.preLive.add("java.net.URLClassLoader"); + + CallGraph.preLive.add("java.security.SecureClassLoader"); + CallGraph.preLive.add("java.security.AccessController"); + + CallGraph.preLive.add("java.text.resources.LocaleElements"); + CallGraph.preLive.add("java.text.resources.LocaleElements_en"); + + CallGraph.preLive.add("java.util.HashMap"); + + CallGraph.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(final String name) { + if (CallGraph.preLive == null) { + CallGraph.init(); + } + CallGraph.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(final String name) { + if (CallGraph.preLive == null) { + CallGraph.init(); + } + return (CallGraph.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(final InlineContext context, final Set roots) { + Assert.isTrue(roots != null, "A call graph must have roots"); + Assert.isTrue(roots.size() > 0, "A call graph must have roots"); + + if (CallGraph.preLive == null) { + CallGraph.init(); + } + + this.context = context; + this.hier = context.getHierarchy(); + this.roots = roots; + + this.liveClasses = new HashSet(); + this.resolvesTo = new HashMap(); + this.calls = new HashMap(); + this.blocked = new HashMap(); + this.worklist = new LinkedList(this.roots); + this.liveMethods = new HashSet(); + + // To save space, make one InstructionVisitor and use it on every + // Instruction. + final CallVisitor visitor = new CallVisitor(this); + + CallGraph.db("Adding pre-live classes"); + doPreLive(); + + CallGraph.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()) { + final MemberRef caller = (MemberRef) worklist.remove(0); + + if (liveMethods.contains(caller)) { + // We've already handled this method + continue; + } + + MethodEditor callerMethod = null; + try { + callerMethod = context.editMethod(caller); + + } catch (final 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; + } + + CallGraph.db("\n Examining method " + caller); + + final Set callees = new HashSet(); // 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.isConstructor()) { + addClinit(callerMethod.declaringClass().type()); + } + + // Examine the instructions in the caller method. + final Iterator code = callerMethod.code().iterator(); + visitor.setCaller(callerMethod); + while (code.hasNext()) { + final Object o = code.next(); + if (o instanceof Instruction) { + final Instruction inst = (Instruction) o; + inst.visit(visitor); + } + } + } + + // 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 (!CallGraph.USEPRELIVE) { + return; + } + + CallGraph.db("Making constructors of pre-live classes live"); + + final Iterator iter = CallGraph.preLive.iterator(); + while (iter.hasNext()) { + String name = (String) iter.next(); + CallGraph.db(" " + name + " is pre-live"); + name = name.replace('.', '/'); + + ClassEditor ce = null; + try { + ce = context.editClass(name); + + } catch (final 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(ce.type()); + addClinit(ce.type()); + + // Make all constructors live + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor method = context.editMethod(methods[i]); + if (method.name().equals("")) { + CallGraph.db(" " + method); + worklist.add(method.memberRef()); + } + } + } + } + + /** + * Adds the static initializer for a given Type to the worklist. + */ + void addClinit(final Type type) { + try { + final ClassEditor ce = context.editClass(type); + + final MethodInfo[] methods = ce.methods(); + for (int i = 0; i < methods.length; i++) { + final MethodEditor clinit = context.editMethod(methods[i]); + if (clinit.name().equals("")) { + worklist.add(clinit.memberRef()); + context.release(clinit.methodInfo()); + break; + } + context.release(clinit.methodInfo()); + } + context.release(ce.classInfo()); + + } catch (final 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(final MethodEditor caller, final MemberRef callee) { + // Figure out which methods the callee can resolve to. + final Iterator resolvesToWith = hier.resolvesToWith(callee).iterator(); + + while (resolvesToWith.hasNext()) { + final ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) resolvesToWith + .next(); + CallGraph.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()) { + final Type rType = (Type) rTypes.next(); + if (liveClasses.contains(rType)) { + isLive = true; + CallGraph.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(); + final StringBuffer sb = new StringBuffer(); + while (rTypes.hasNext()) { + final Type rType = (Type) rTypes.next(); + Set blockedMethods = (Set) blocked.get(rType); + if (blockedMethods == null) { + blockedMethods = new HashSet(); + blocked.put(rType, blockedMethods); + } + blockedMethods.add(rtw.method); + sb.append(rType.toString()); + if (rTypes.hasNext()) { + sb.append(','); + } + } + CallGraph.db(" Blocked " + rtw.method + " on " + sb); + } + } + } + + /** + * Makes note of one method calling another. This does not make the method + * live. + */ + void addCall(final MethodEditor callerMethod, final MemberRef callee) { + // Just maintain the calls mapping + final MemberRef caller = callerMethod.memberRef(); + Set callees = (Set) this.calls.get(caller); + if (callees == null) { + callees = new HashSet(); + 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(final Type type) { + if (this.liveClasses.contains(type)) { + return; + } + + // Make type live and unblock all methods blocked on it + CallGraph.db(" Making " + type + " live"); + liveClasses.add(type); + final Set blockedMethods = (Set) blocked.remove(type); + if (blockedMethods != null) { + final Iterator iter = blockedMethods.iterator(); + while (iter.hasNext()) { + final MemberRef method = (MemberRef) iter.next(); + CallGraph.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(final MemberRef method) { + TreeSet resolvesTo = (TreeSet) this.resolvesTo.get(method); + + if (resolvesTo == null) { + resolvesTo = new TreeSet(new MemberRefComparator(context)); + this.resolvesTo.put(method, resolvesTo); + + final Set liveMethods = this.liveMethods(); + final Iterator rtws = hier.resolvesToWith(method).iterator(); + while (rtws.hasNext()) { + final 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(final MemberRef method, final 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. + final TreeSet resolvesTo = new TreeSet(new MemberRefComparator(context)); + + final Set liveMethods = this.liveMethods(); + final Iterator rtws = hier.resolvesToWith(method).iterator(); + while (rtws.hasNext()) { + final ClassHierarchy.ResolvesToWith rtw = (ClassHierarchy.ResolvesToWith) rtws + .next(); + if (liveMethods.contains(rtw.method)) { + final 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(final PrintWriter out, boolean printLeaves) { + + final Iterator callers = calls.keySet().iterator(); + while (callers.hasNext()) { + final MemberRef caller = (MemberRef) callers.next(); + + final Iterator callees = ((Set) calls.get(caller)).iterator(); + + if (!printLeaves && !callees.hasNext()) { + continue; + } + + out.print(caller.declaringClass() + "." + caller.name() + + caller.type()); + if (roots.contains(caller)) { + out.print(" (root)"); + } + out.println(""); + + while (callees.hasNext()) { + final MemberRef callee = (MemberRef) callees.next(); + + // Only print live methods + if (!calls.containsKey(callee)) { + continue; + } + + out.println(" " + callee.declaringClass() + "." + + callee.name() + callee.type()); + } + + out.println(""); + } + } + + /** + * Prints a summary of the call graph. Including the classes that are live + * and which methods are blocked. + */ + public void printSummary(final PrintWriter out) { + out.println("Instantiated classes:"); + final Iterator instantiated = this.liveClasses.iterator(); + while (instantiated.hasNext()) { + final Type type = (Type) instantiated.next(); + out.println(" " + type.toString()); + } + + out.println("\nBlocked methods:"); + if (blocked != null) { + final Iterator types = blocked.keySet().iterator(); + while (types.hasNext()) { + final Type type = (Type) types.next(); + out.println(" " + type); + + final Set set = (Set) blocked.get(type); + if (set != null) { + final Iterator methods = set.iterator(); + while (methods.hasNext()) { + final MemberRef method = (MemberRef) 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 { + MethodEditor caller; + + CallGraph cg; + + boolean firstSpecial; // Are we dealing with the first invokespecial? + + private static void db(final String s) { + CallGraph.db(s); + } + + public CallVisitor(final CallGraph cg) { + this.cg = cg; + } + + public void setCaller(final MethodEditor caller) { + this.caller = caller; + if (caller.isConstructor()) { + this.firstSpecial = true; + } else { + this.firstSpecial = false; + } + } + + public void visit_invokevirtual(final Instruction inst) { + CallVisitor.db("\n Visiting Call: " + inst); + + this.firstSpecial = false; + + // Call doVirtual to determine which methods this call may resolve + // to are live. + final MemberRef callee = (MemberRef) inst.operand(); + cg.doVirtual(caller, callee); + } + + public void visit_invokeinterface(final Instruction inst) { + CallVisitor.db("\n Visiting Call: " + inst); + + this.firstSpecial = false; + + // Pretty much the same as invokevirtual + final MemberRef callee = (MemberRef) inst.operand(); + cg.doVirtual(caller, callee); + } + + public void visit_invokestatic(final Instruction inst) { + CallVisitor.db("\n Visiting call: " + inst); + + this.firstSpecial = false; + + // There's not a lot to do with static methods since there is no + // dynamic dispatch. + final MemberRef callee = (MemberRef) inst.operand(); + cg.addCall(caller, callee); + cg.worklist.add(callee); + } + + public void visit_invokespecial(final Instruction inst) { + CallVisitor.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. + final MemberRef callee = (MemberRef) inst.operand(); + + MethodEditor calleeMethod = null; + + try { + calleeMethod = cg.context.editMethod(callee); + + } catch (final 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.methodInfo()); + } + + public void visit_getstatic(final Instruction inst) { + // Referencing a static fields implies that its class's static + // initializer has been invoked. + CallVisitor.db("\n Referencing static field " + inst); + final MemberRef field = (MemberRef) inst.operand(); + cg.addClinit(field.declaringClass()); + } + + public void visit_putstatic(final Instruction inst) { + // Referencing a static field implies that its class's static + // initializer has been invoked. + CallVisitor.db("\n Referencing static field " + inst); + final MemberRef field = (MemberRef) inst.operand(); + cg.addClinit(field.declaringClass()); + } + + public void visit_new(final Instruction inst) { + // The new instruction instantiates a type and thus makes it live. + final Type type = (Type) inst.operand(); + cg.makeLive(type); + } +} + +/** + * Compares MemberRefs such that overriding methods are less than + * overridden methods. + */ +class MemberRefComparator implements Comparator { + TypeComparator c; + + public MemberRefComparator(final InlineContext context) { + c = new TypeComparator(context); + } + + public int compare(final Object o1, final Object o2) { + Assert.isTrue(o1 instanceof MemberRef, o1 + " is not a MemberRef"); + Assert.isTrue(o2 instanceof MemberRef, o2 + " is not a MemberRef"); + + final MemberRef ref1 = (MemberRef) o1; + final MemberRef ref2 = (MemberRef) o2; + + final Type type1 = ref1.declaringClass(); + final Type type2 = ref2.declaringClass(); + + return (c.compare(type1, type2)); + } + + public boolean compareTo(final 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..cc94598 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/Inline.java @@ -0,0 +1,733 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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 maxCallDepth; // Max of height of call stack + + private boolean inlineExceptions; // Inline methods that throw exceptions + + private InlineContext context; + + private Map editors; // Maps MemberRefs to their MethodEditors + + /** + * Size of the largest method that can be inlined + */ + public static int CALLEE_SIZE = 100000; + + private static void db(final String s) { + if (Inline.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(final InlineContext context, final int maxCodeSize) { + this.context = context; + this.maxCodeSize = maxCodeSize; + this.maxCallDepth = 1; + this.inlineExceptions = true; + + editors = new HashMap(); + } + + /** + * Sets the maximum of size of a method that will be inlined. No method + * larger than this will be inlined. + */ + public void setMaxInlineSize(final int maxInlineSize) { + } + + /** + * Sets the maximum number of nested calls we inline. + */ + public void setMaxCallDepth(final 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(final boolean inlineExceptions) { + this.inlineExceptions = inlineExceptions; + } + + /** + * Scans a method and inlines non-virtual method calls according to this + * Inline's metrics. + */ + public void inline(final MethodEditor method) { + // Go through the method and look for calls to inline + StackHeightCounter stackHeight = new StackHeightCounter(method); + List code = method.code(); + boolean firstCall = true; + for (int i = 0; i < code.size(); i++) { + final Object o = code.get(i); + if (o instanceof Instruction) { + final Instruction inst = (Instruction) o; + if ((inst.opcodeClass() == Opcode.opcx_invokestatic) + || (inst.opcodeClass() == Opcode.opcx_invokespecial)) { + final MemberRef callee = (MemberRef) inst.operand(); + final Stack callStack = new Stack(); + callStack.add(method.memberRef()); + + Inline.db(" Call: " + inst); + + stackHeight.handle(inst); + final int expectedHeight = stackHeight.height(); + stackHeight.unhandle(inst); + + final int j = i; + i = inline(method, callee, i, callStack, stackHeight, + firstCall); + + if (j == i) { + // Call was not inlined, add it to the stack + stackHeight.handle(inst); + Inline.db(" " + i + "." + stackHeight.height() + ") " + + inst); + } + + final 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(inst); + Inline.db(" " + i + "." + stackHeight.height() + ") " + + inst); + } + + if (inst.isInvoke()) { + firstCall = false; + } + + } else if (o instanceof Label) { + final Label label = (Label) o; + stackHeight.handle(label); + Inline.db(" " + i + "." + stackHeight.height() + ") " + label + + (label.startsBlock() ? " (starts block)" : "")); + } + + } + + method.setCode(code); + + if (Inline.DEBUG) { + stackHeight = new StackHeightCounter(method); + Inline.db("\nNew Code for " + method.declaringClass().name() + "." + + method.name() + method.type()); + code = method.code(); + for (int j = 0; j < code.size(); j++) { + if (code.get(j) instanceof Label) { + final Label label = (Label) code.get(j); + + stackHeight.handle(label); + + final Iterator tryCatches = method.tryCatches().iterator(); + while (tryCatches.hasNext()) { + final TryCatch tryCatch = (TryCatch) tryCatches.next(); + if (tryCatch.start().equals(label)) { + System.out.println(" Begin protected region"); + + } + + if (tryCatch.end().equals(label)) { + System.out.println(" End protected region"); + + } + + // A Label can both end a protected region and begin + // catch + // block + + if (tryCatch.handler().equals(label)) { + System.out.println(" Catch " + tryCatch.type()); + } + + } + + System.out.println(" " + j + "." + stackHeight.height() + + ") " + label + + (label.startsBlock() ? " (starts block)" : "")); + } else { + final Instruction inst = (Instruction) code.get(j); + stackHeight.handle(inst); + System.out.println(" " + j + "." + stackHeight.height() + + ") " + code.get(j)); + } + } + + // Print try-catch information + final Iterator tryCatches = method.tryCatches().iterator(); + System.out.println("Exception information:"); + while (tryCatches.hasNext()) { + final TryCatch tryCatch = (TryCatch) tryCatches.next(); + System.out.println(" " + tryCatch); + } + 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. + */ + private int inline(final MethodEditor caller, final MemberRef callee, + int index, final Stack callStack, + final StackHeightCounter stackHeight, boolean firstCall) { + + Instruction newInst = null; + + // Do we ignore the method being inlined? + if (context.ignoreMethod(callee)) { + Inline.db(" Can't inline " + callee + ": it's ignored"); + return (index++); + } + + // Can we inline this method + if (callStack.size() > maxCallDepth) { + Inline.db(" Can't inline " + callee + ": max call depth (" + + maxCallDepth + ") reached"); + return (index++); + + } else if (callStack.contains(callee)) { + Inline.db(" Can't inline recursive call to " + callee); + return (index++); + } + + // Make sure we're not inlining the static-ized version of a + // method in the call stack. + String name = callee.name(); + final int b = name.indexOf("$$BLOAT"); + if (b != -1) { + name = name.substring(0, b); + + // Get rid of first parameter + final Type[] oldParams = callee.type().paramTypes(); + final StringBuffer sb = new StringBuffer("("); + for (int p = 1; p < oldParams.length; p++) { + sb.append(oldParams[p].descriptor()); + } + sb.append(")" + callee.type().returnType()); + final Type newType = Type.getType(sb.toString()); + + final MemberRef unBloated = new MemberRef(callee.declaringClass(), + new NameAndType(name, newType)); + + if (callStack.contains(unBloated)) { + Inline.db(" Can't inline recursive call to " + callee); + return (index++); + } + } + + final List code = caller.code(); + if (code.size() > maxCodeSize) { + Inline.db(" Can't inline " + callee + ": max code size (" + + maxCodeSize + ") reached"); + return (index++); + } + + MethodEditor calleeMethod = null; + try { + calleeMethod = context.editMethod(callee); + + } catch (final NoSuchMethodException ex) { + System.err.println("Couldn't find method " + callee); + ex.printStackTrace(System.err); + System.exit(1); + } + + if (calleeMethod.isNative()) { + Inline.db(" Can't inline " + callee + ": it's a native method"); + return (index++); + } + + if (calleeMethod.isSynchronized()) { + Inline.db(" Can't inline " + callee + ": it's synchronized"); + return (index++); + } + + if (!inlineExceptions + && (calleeMethod.methodInfo().exceptionTypes().length > 0)) { + Inline.db(" Can't inline " + callee + + ": it may throw an exception"); + return (index++); + } + + if (calleeMethod.code().size() > Inline.CALLEE_SIZE) { + Inline.db(" Can't inline " + callee + ": it's too big"); + 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.tryCatches().size() > 0) { + if (stackHeight.height() > callee.type().stackHeight()) { + Inline.db(" Can't inline " + callee + + ": It catches an exception and there's stuff on the " + + "stack"); + 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. + final Iterator tryCatches0 = calleeMethod.tryCatches().iterator(); + while (tryCatches0.hasNext()) { + final TryCatch tc1 = (TryCatch) tryCatches0.next(); + final Iterator iter = stackHeight.tryCatches().iterator(); + while (iter.hasNext()) { + final TryCatch tc2 = (TryCatch) iter.next(); + final Type t1 = tc1.type(); + final Type t2 = tc2.type(); + if ((t1 != null) && (t2 != null) && t1.equals(t2)) { + Inline.db(" Can't inline " + callee + + ": It catches the same type " + + tc1.type().className() + + " as the current protected region"); + 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.isConstructor() + && (!firstCall || !caller.isConstructor())) { + + Inline.db(" Can't inline " + callee + + ": It calls a normal constructor"); + 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. + final Instruction call = (Instruction) code.remove(index--); + Inline.db(" Removing call: " + call); + Assert.isTrue((call.opcodeClass() == Opcode.opcx_invokestatic) + || (call.opcodeClass() == Opcode.opcx_invokespecial), + "Removing the wrong call instruction:" + call); + callStack.push(callee); + + Inline + .db(" Inlining call (" + callStack.size() + ") to " + + callee.declaringClass() + "." + callee.name() + + callee.type()); + context.getInlineStats().noteInlined(); + + // 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. + final Mapper mapper = new Mapper(caller); + Type[] paramTypes = callee.type().indexedParamTypes(); + if (!calleeMethod.isStatic()) { + // Constructors (and any other special methods we're inlining) + // have a "this" pointer where static methods do not. + final Type[] newParams = new Type[paramTypes.length + 1]; + newParams[0] = callee.declaringClass(); + + for (int i = 0; i < paramTypes.length; i++) { + newParams[i + 1] = paramTypes[i]; + } + paramTypes = newParams; + } + + final LocalVariable[] params = new LocalVariable[paramTypes.length]; + + Inline.db(" Indexed params:"); + for (int i = 0; i < params.length; i++) { + params[i] = calleeMethod.paramAt(i); + Inline.db(" " + i + ": " + params[i] + + (params[i] != null ? " " + params[i].type() + " " : "")); + } + + for (int i = params.length - 1; i >= 0; i--) { + // Map the local variables containing the arguments to new + // local variables. + final LocalVariable param = params[i]; + final Type paramType = params[i].type(); + + if (param.type() == null) { + continue; + } + + Inline.db(" Param " + i + ": " + param + " of type " + paramType); + + final LocalVariable newVar = mapper.map(param, paramType); + + int opcode; + + if (paramType.isReference()) { + opcode = Opcode.opcx_astore; + + } else { + switch (paramType.typeCode()) { + case Type.BOOLEAN_CODE: + case Type.BYTE_CODE: + case Type.CHARACTER_CODE: + case Type.SHORT_CODE: + opcode = Opcode.opcx_istore; + break; + + case Type.DOUBLE_CODE: + opcode = Opcode.opcx_dstore; + break; + + case Type.LONG_CODE: + opcode = Opcode.opcx_lstore; + break; + + case Type.FLOAT_CODE: + opcode = Opcode.opcx_fstore; + break; + + case Type.INTEGER_CODE: + opcode = Opcode.opcx_istore; + break; + + default: + throw new IllegalArgumentException("What's a " + paramType + + "doing as a method " + "parameter"); + } + } + + newInst = new Instruction(opcode, newVar); + code.add(++index, newInst); + stackHeight.handle(newInst); + Inline.db(" " + index + "." + stackHeight.height() + "> " + + newInst); + } + + // Before we mess with the code, we have to patch up the try-catch + // information from the inlined method to the caller method. + final Iterator tryCatches = calleeMethod.tryCatches().iterator(); + while (tryCatches.hasNext()) { + final TryCatch tryCatch = (TryCatch) tryCatches.next(); + + final Label start = mapper.map(tryCatch.start()); + final Label end = mapper.map(tryCatch.end()); + final Label handler = mapper.map(tryCatch.handler()); + + final TryCatch newTryCatch = new TryCatch(start, end, handler, + tryCatch.type()); + caller.addTryCatch(newTryCatch); + + // db("Try-catch"); + // db(" Before: " + tryCatch.start() + "\t" + tryCatch.end() + + // "\t" + tryCatch.handler()); + // db(" After: " + newTryCatch.start() + "\t" + newTryCatch.end() + // + "\t" + newTryCatch.handler()); + } + + // 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. + + final List inlineCode = calleeMethod.code(); + + // 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. + final Object last = inlineCode.get(inlineCode.size() - 1); + boolean addEndLabel; + Label endLabel; + if (last instanceof Label) { + endLabel = mapper.map((Label) last); + addEndLabel = false; + + } else { + endLabel = caller.newLabel(); + addEndLabel = true; + } + endLabel.setStartsBlock(true); + + firstCall = true; + + for (int j = 0; j < inlineCode.size(); j++) { + final Object o = inlineCode.get(j); + + if (o instanceof Label) { + final Label label = (Label) o; + final Label newLabel = mapper.map(label); + + code.add(++index, newLabel); + stackHeight.handle(newLabel); + Inline.db(" " + index + "." + stackHeight.height() + "> " + + newLabel + + (newLabel.startsBlock() ? " (starts block)" : "")); + continue; + } + + Assert.isTrue(o instanceof Instruction, "What is a " + o + + " doing in the instruction stream?"); + + final Instruction inst = (Instruction) inlineCode.get(j); + Object operand = inst.operand(); + final int opcode = inst.opcodeClass(); + + if (operand instanceof LocalVariable) { + // Map local variable in the callee method to local + // variables in the caller method. + final LocalVariable local = mapper.map((LocalVariable) operand, + (inst.category() == 2 ? true : false)); + operand = local; + + } else if (operand instanceof Label) { + // Map labels in the callee method to labels in the caller + // method. + final Label label = mapper.map((Label) operand); + operand = label; + + } else if (operand instanceof IncOperand) { + // Map the local being incremented + final IncOperand inc = (IncOperand) operand; + final LocalVariable newLocal = mapper.map(inc.var(), + Type.INTEGER); + operand = new IncOperand(newLocal, inc.incr()); + + } else if (operand instanceof Switch) { + // We have to patch up the Labels involved with the Switch + final Switch oldSwitch = (Switch) operand; + + final Label newDefault = mapper.map(oldSwitch.defaultTarget()); + + final Label[] oldTargets = oldSwitch.targets(); + final Label[] newTargets = new Label[oldTargets.length]; + for (int i = 0; i < newTargets.length; i++) { + final Label newTarget = mapper.map(oldTargets[i]); + newTargets[i] = newTarget; + } + + operand = new Switch(newDefault, newTargets, oldSwitch.values()); + } + + if (inst.isReturn()) { + // 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 Instruction(Opcode.opcx_goto, endLabel); + code.add(++index, newInst); + stackHeight.handle(newInst); + Inline.db(" " + index + "." + stackHeight.height() + "> " + + newInst); + + } else if ((inst.opcodeClass() == Opcode.opcx_invokestatic) + || (inst.opcodeClass() == Opcode.opcx_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); + + stackHeight.handle(newInst); + final int expectedHeight = stackHeight.height(); + stackHeight.unhandle(newInst); + + final MemberRef nestedCall = (MemberRef) inst.operand(); + final int oldIndex = index; + index = inline(caller, nestedCall, index, callStack, + stackHeight, firstCall); + + if (index == oldIndex) { + stackHeight.handle(newInst); + Inline.db(" " + index + "." + stackHeight.height() + "> " + + newInst); + } + + final 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(newInst); + Inline.db(" " + index + "." + stackHeight.height() + "> " + + newInst); + } + + // We want to do this after we've made any recursive calls to + // inline. + if (inst.isInvoke()) { + firstCall = false; + } + + } + + if (addEndLabel) { + // Done inlining. Add end label. + code.add(++index, endLabel); + stackHeight.handle(endLabel); + Inline.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 MethodEditor method; // Method into which things are mapped + + private int offset; // Start numbering new locals here + + private static void db(final String s) { + if (Inline.DEBUG) { + System.out.println(s); + } + } + + /** + * Constructor. + */ + public Mapper(final MethodEditor method) { + this.method = method; + varsMap = new HashMap(); + labelsMap = new HashMap(); + offset = method.maxLocals() + 1; + } + + public Label map(final Label label) { + Label newLabel = (Label) labelsMap.get(label); + if (newLabel == null) { + newLabel = this.method.newLabel(); + newLabel.setStartsBlock(label.startsBlock()); + labelsMap.put(label, newLabel); + Mapper.db(" " + label + " -> " + newLabel + + (newLabel.startsBlock() ? " (starts block)" : "")); + } + return (newLabel); + } + + public LocalVariable map(final LocalVariable var, final Type type) { + LocalVariable newVar = (LocalVariable) varsMap.get(var); + if (newVar == null) { + newVar = this.method.localAt(var.index() + offset); + // newVar = this.method.newLocal(type); + varsMap.put(var, newVar); + Mapper.db(" " + var + " (" + var.index() + ") -> " + newVar + + "(" + var.index() + "+" + offset + ")" + + (type.isWide() ? " (" + type + ")" : "")); + } + return (newVar); + } + + public LocalVariable map(final LocalVariable var, final 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); + Mapper.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..5ec55ca --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/InlineContext.java @@ -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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(MemberRef 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(MemberRef 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..ce058f2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/InlineStats.java @@ -0,0 +1,127 @@ +/** + * 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. + * + * 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 + */ + +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 + + 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(final 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(final int morphicity) { + final Integer r = new Integer(morphicity); + final 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(final int nLiveMethods) { + this.nLiveMethods = nLiveMethods; + } + + /** + * Notes the number of live classes. + */ + public void noteLiveClasses(final int nLiveClasses) { + this.nLiveClasses = nLiveClasses; + } + + /** + * Print a summary of the statistics to a PrintWriter. + */ + public void printSummary(final 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:"); + + final Iterator morphs = this.morphicity.keySet().iterator(); + int total = 0; + while (morphs.hasNext()) { + final Integer morph = (Integer) morphs.next(); + final 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/InstructionStack.java b/src/EDU/purdue/cs/bloat/inline/InstructionStack.java new file mode 100644 index 0000000..5b4e123 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/InstructionStack.java @@ -0,0 +1,1476 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * The of InstructionStack keeps track of which instructions pushed a + * certain element of the stack. It is a stack of sets of instructions. You can + * think of it like this: (1, {4,6}, 8) means that instruction 1 pushed the item + * on the bottom of the stack, instructions 4 and 6 both push the second element + * on the stack (depending on control flow), and instruction 8 pushed the + * element on top of the stack. We use this information at a call site to + * determine what instruction(s) pushed the receiver object onto the stack. + * Special thanks to Jan Vitek for helping me come up with this algorithm. + * + *

+ * + * This class is an InstructionVisitor that updates the instruction + * stack representation appropriately. When there is a merge in control flow, + * two InstructionStacks are merged using the merge + * method. + * + *

+ * + * This class is also used to determine whether the object at a given stack + * depth is "preexistent". An object preexists if we can guarantee that it was + * created outside of the method in which it is used. While it is possible to + * determine which fields are preexistent (see "Inlining of Virtual Methods" by + * Detlefs and Ageson in ECOOP99), we only keep track of local variables that + * preexist. + * + *

+ * + * We determine which local variables preexist as follows. Initially, only the + * local variables for method parameters preexist. When a store is encoutered, + * we determine if the set of instructions on top of the stack consist of loads + * preexistent variables. If so, then the variable being stored into is + * preexistent. However, objects that are the result of an allocation + * (constructor call) in the method are preexist. Thus, we maintain the preexist + * information as a set. If the set is null, then the object does not preexist. + * If the set is empty, then it preexists and came from at least one argument. + * If the set is non-empty, then it contains the type(s) of the constructor(s) + * from which it originated. Pretty neat, huh? + */ +public class InstructionStack extends InstructionAdapter { + + MethodEditor method; // Method we're dealing with + + HashMap stacks; // Maps Labels to their stacks + + LinkedList currStack; // The current stack + + HashMap preexists; // Maps Labels to their preexists + + HashMap currPreexists; // The current preexist (var -> Set) + + private static void pre(final String s) { + // Debug preexistence + if (false) { + System.out.println(s); + } + } + + /** + * Constructor. Creates an empty InstructionStack. + */ + public InstructionStack(final MethodEditor method) { + this.method = method; + this.stacks = new HashMap(); + this.preexists = new HashMap(); + + // Initially only the parameters to the method prexist + final Type[] paramTypes = method.paramTypes(); + this.currPreexists = new HashMap(); + for (int i = 0; i < paramTypes.length; i++) { + // We only care about the preexistence of objects (not arrays) + if ((paramTypes[i] != null) && paramTypes[i].isObject()) { + this.currPreexists.put(method.paramAt(i), new HashSet()); + } + } + } + + /** + * Deals with a Label. + */ + public void handle(final Label label) { + final LinkedList stack = (LinkedList) stacks.get(label); + + if (stack == null) { + // If this label starts an exception handler, account for the + // exception being pushed on the stack + final Iterator tryCatches = method.tryCatches().iterator(); + while (tryCatches.hasNext()) { + final TryCatch tc = (TryCatch) tryCatches.next(); + + if (tc.handler().equals(label)) { + // Kluge to push the exception object on the stack. I don't + // think it matters much. + final Instruction aload = new Instruction(Opcode.opcx_ldc, + "Exception"); + currStack = new LinkedList(); + aload.visit(this); + stacks.put(label, stack); + label.setStartsBlock(true); + + // We have no idea from where the exception will be thrown, + // so we can't make any assumptions about the preexistence + // of any variables. + currPreexists = new HashMap(); + return; + } + } + + if (currStack == null) { + // Make a new stack + currStack = new LinkedList(); + stacks.put(label, currStack); + + // I don't think we need to worry about the currPreexists. It + // was taken care of in the constructor. + + } else { + // Otherwise, keep the current stack + currStack = (LinkedList) currStack.clone(); + stacks.put(label, currStack); + + // And the current preexists + currPreexists = InstructionStack.clonePreexists(currPreexists); + this.preexists.put(label, currPreexists); + } + + } else { + // Merge the old stack with the current one + currStack = InstructionStack.merge(currStack, stack); + stacks.put(label, currStack); + + final HashMap oldPreexists = (HashMap) this.preexists.get(label); + currPreexists = InstructionStack.merge(oldPreexists, currPreexists); + this.preexists.put(label, currPreexists); + } + } + + /** + * Deals with an Instruction handles branches, jsrs, and the + * like. + */ + public void handle(final Instruction inst) { + // Visit first + inst.visit(this); + + if (inst.isJump()) { + final Label target = (Label) inst.operand(); + target.setStartsBlock(true); + + // Merge the target's stack with any other stacks at that + // label + final LinkedList targetStack = (LinkedList) stacks.get(target); + if (targetStack != null) { + // Don't change currStack, but do the merge + stacks.put(target, InstructionStack.merge(currStack, + targetStack)); + + final HashMap oldPreexists = (HashMap) this.preexists + .get(target); + this.preexists.put(target, InstructionStack.merge( + currPreexists, oldPreexists)); + + } else { + // Put a new stack at the target + stacks.put(target, currStack.clone()); + this.preexists.put(target, InstructionStack + .clonePreexists(currPreexists)); + } + + if (!inst.isConditionalJump()) { + // The next instruction should be a Label. But since it is + // not the next instruction executed, we don't want to merge + // the contents of the label's stack and the current stack. + // So, null out the current stack. + currStack = new LinkedList(); + } + + } else if (inst.isSwitch()) { + // Propagate the current stack to all targets + final Switch sw = (Switch) inst.operand(); + final Label defaultTarget = sw.defaultTarget(); + defaultTarget.setStartsBlock(true); + + final LinkedList defaultStack = (LinkedList) stacks + .get(defaultTarget); + if (defaultStack != null) { + Assert.isTrue(defaultStack.size() == currStack.size(), + "Stack height mismatch (" + defaultStack.size() + + " != " + currStack.size() + ") at " + inst); + // Merge stacks for good measure + stacks.put(defaultTarget, InstructionStack.merge(currStack, + defaultStack)); + + final HashMap defaultPreexists = (HashMap) this.preexists + .get(defaultTarget); + this.preexists.put(defaultTarget, InstructionStack.merge( + currPreexists, defaultPreexists)); + + } else { + // Put copy of stack at target + stacks.put(defaultTarget, currStack.clone()); + this.preexists.put(defaultTarget, InstructionStack + .clonePreexists(currPreexists)); + } + + final Label[] targets = sw.targets(); + for (int t = 0; t < targets.length; t++) { + final Label target = targets[t]; + target.setStartsBlock(true); + final LinkedList targetStack = (LinkedList) stacks.get(target); + if (targetStack != null) { + Assert.isTrue(targetStack.size() == currStack.size(), + "Stack height mismatch (" + targetStack.size() + + " != " + currStack.size() + ") at " + + inst); + // Merge stacks for good measure + stacks.put(target, InstructionStack.merge(currStack, + targetStack)); + + final HashMap oldPreexists = (HashMap) this.preexists + .get(target); + this.preexists.put(target, InstructionStack.merge( + oldPreexists, currPreexists)); + + } else { + stacks.put(target, currStack.clone()); + this.preexists.put(target, InstructionStack + .clonePreexists(currPreexists)); + } + } + + } else if (inst.isJsr()) { + // Someday we might have to deal with subroutines that push + // stuff on the stack. That complicates things. I'm going to + // pretend it doesn't exist. It was good enough for Nate. + + // In the meantime, we have to propagate the fact that the jsr + // pushes the return address to the subroutine. We use an empty + // stack because it is possible that a subroutine could be + // called with different stack heights. Here is another thing + // that needs to be fixed someday. + final LinkedList subStack = new LinkedList(); + final LinkedList oldStack = currStack; + + // Push the return address on stack + currStack = subStack; + inst.visit(this); + + currStack = oldStack; // Should be okay unless sub effects stack + + // Propagate subStack to subroutine + final Label subroutine = (Label) inst.operand(); + subroutine.setStartsBlock(true); + stacks.put(subroutine, subStack); + this.preexists.put(subroutine, new HashMap()); + + } else if (inst.isReturn() || inst.isThrow()) { + // We don't what comes next, but we don't want to merge with the + // current stack. + currStack = new LinkedList(); + } + } + + /** + * Pushes an instruction onto the stack + */ + private void push(final Instruction inst) { + // Create a new set for the top element of the stack + final Set set = new HashSet(); + set.add(inst); + currStack.add(set); + } + + /** + * Pops the top of the stack. + */ + private void pop() { + currStack.removeLast(); + } + + /** + * Pops a given number of elements off the stack. + */ + private void pop(final int n) { + for (int i = 0; i < n; i++) { + currStack.removeLast(); + } + } + + /** + * Returns the number of elements in this instruction stack. + */ + public int height() { + return (currStack.size()); + } + + /** + * Returns the set of Instructions at depth n of the + * instruction stack. Depth 0 is the top of the stack. The bottom of the + * stack is at depth stackSize - 1. + */ + public Set atDepth(final int n) { + final Set set = (Set) currStack.get(currStack.size() - 1 - n); + return (set); + } + + /** + * Returns a Set representing whether or not the instructions at + * a given depth push a preexistent object onto the stack. If the list is + * null, then the push is not preexistent. If the list is empty, + * then the push is preexistent. If the list is non-empty, it contains the + * Type(s) of objects that are known to be on the stack. These + * types are the results of calls to constructors. + */ + public HashSet preexistsAtDepth(final int n) { + // How do we determine whether a set of instructions pushes a + // preexist object? All of the instructions must be loads of + // objects from preexistent variable or the result of an + // object creation. Note that we can deal with arrays because + // we'd have to keep track of indices. + + InstructionStack.pre(" Preexisting variables: " + + InstructionStack.db(currPreexists)); + + HashSet atDepth = null; + final Iterator insts = this.atDepth(n).iterator(); + Assert.isTrue(insts.hasNext(), "No instructions at depth " + n); + while (insts.hasNext()) { + final Instruction inst = (Instruction) insts.next(); + InstructionStack.pre(" Instruction at " + n + ": " + inst); + if (inst.opcodeClass() == Opcode.opcx_aload) { + final LocalVariable var = (LocalVariable) inst.operand(); + final Set set = (Set) this.currPreexists.get(var); + if (set != null) { + if (set.isEmpty()) { + // If the set is empty, then this local variable came + // from + // a method argument. + atDepth = new HashSet(); + + } else { + // The list contains types that are the result of a + // constructor call. Add them to the preexists list. + if (atDepth == null) { + atDepth = new HashSet(); + } + atDepth.addAll(set); + } + continue; + } + + // Instruction loads a non-preexistent variable, fall through + + } else if (inst.opcodeClass() == Opcode.opcx_new) { + // We look for the new instruction instead of the constructor + // call because of the way we represent the stack. + + if ((atDepth != null) && atDepth.isEmpty()) { + // We already know that the object pushed at this depth are + // one of the arguments. We don't the exact type of the + // argument, so we can't safely add the type being + // instantiated to the preexist list. + continue; + } + + // Figure out the type being created and add it to the + // preexists list. + final Type type = (Type) inst.operand(); + InstructionStack.pre(" Constructing " + + Type.truncatedName(type)); + if (atDepth == null) { + atDepth = new HashSet(); + } + atDepth.add(type); + continue; + + // A non-constructor invokespecial was called, fall through + + } else if (inst.opcodeClass() == Opcode.opcx_dup) { + final Set set = this.preexistsAtDepth(n - 1); + if (set != null) { + if (set.isEmpty()) { + // If list is empty, then this preexist must also be + // empty + atDepth = new HashSet(); + + } else { + // Add the classes instantiated to the list + atDepth.addAll(set); + } + continue; + } + } + + InstructionStack.pre(" Doesn't preexist"); + return (null); + } + + // If we got down here every instruction was preexistent + InstructionStack.pre(" Preexists"); + return (atDepth); + + } + + /** + * Merges two stacks together and returns their union. Note that stacks of + * unequal height cannot be merged. + */ + private static LinkedList merge(final LinkedList stack1, + final LinkedList stack2) { + + Assert.isFalse((stack1 == null) && (stack2 == null), + "Cannot merge two null stacks"); + + final LinkedList merge = new LinkedList(); + + // If either stack is null or empty, just use the other one + if ((stack1 == null) || (stack1.size() == 0)) { + merge.addAll(stack2); + return (merge); + } + + if ((stack2 == null) || (stack2.size() == 0)) { + merge.addAll(stack1); + return (merge); + } + + Assert.isTrue(stack1.size() == stack2.size(), + "Stacks of unequal height cannot be merged (" + stack1.size() + + " != " + stack2.size() + ")"); + + for (int i = 0; i < stack1.size(); i++) { + final Set mergeSet = new HashSet(); + mergeSet.addAll((Set) stack1.get(i)); + mergeSet.addAll((Set) stack2.get(i)); + merge.add(i, mergeSet); + } + + return (merge); + } + + /** + * Merges two preexists lists. For a given variable, if either of the two + * input indices is null, then the result is null. + * If either of the two input indices is empty, then the result is empty. + * Otherwise, the result is the union of the two input sets. + */ + private static HashMap merge(final HashMap one, final HashMap two) { + Assert.isFalse((one == null) && (two == null), + "Can't merge null preexists"); + + if (one == null) { + return (InstructionStack.clonePreexists(two)); + + } else if (two == null) { + return (InstructionStack.clonePreexists(one)); + } + + // Go through all of the variables in both sets. If one is not + // contained in the other, then the set (or null) from the other + // is used. If one is mapped to null, then the result is null. + // If one has an empty set, then the result has an empty set. + // Otherwise, the two non-empty sets are merge. + final HashMap result = new HashMap(); + final Set allVars = new HashSet(); + allVars.addAll(one.keySet()); + allVars.addAll(two.keySet()); + final Iterator iter = allVars.iterator(); + while (iter.hasNext()) { + final LocalVariable var = (LocalVariable) iter.next(); + if (!one.containsKey(var)) { + HashSet set = (HashSet) two.get(var); + if (set != null) { + set = (HashSet) set.clone(); + } + + result.put(var, set); + + } else if (!two.containsKey(var)) { + HashSet set = (HashSet) one.get(var); + if (set != null) { + set = (HashSet) set.clone(); + } + + result.put(var, set); + + } else { + final HashSet oneSet = (HashSet) one.get(var); + final HashSet twoSet = (HashSet) two.get(var); + if ((oneSet == null) || (twoSet == null)) { + result.put(var, null); + + } else if (oneSet.isEmpty() || twoSet.isEmpty()) { + result.put(var, new HashSet()); + + } else { + final Set set = new HashSet(); + set.addAll(oneSet); + set.addAll(twoSet); + result.put(var, set); + } + } + } + + InstructionStack.pre("Merge of " + InstructionStack.db(one) + " and " + + InstructionStack.db(two) + " is " + + InstructionStack.db(result)); + + return (result); + } + + /** + * Returns a textual representation of a preexists mapping. + */ + static String db(final HashMap preexists) { + if (preexists == null) { + return ("\n null?\n"); + } + + final StringBuffer sb = new StringBuffer("\n"); + final Iterator vars = preexists.keySet().iterator(); + while (vars.hasNext()) { + final LocalVariable var = (LocalVariable) vars.next(); + final Set set = (Set) preexists.get(var); + if (set == null) { + sb.append(" " + var + ": null\n"); + + } else { + sb.append(" " + var + ": "); + final Iterator iter = set.iterator(); + while (iter.hasNext()) { + final Type type = (Type) iter.next(); + sb.append(Type.truncatedName(type)); + if (iter.hasNext()) { + sb.append(", "); + } + } + sb.append("\n"); + } + } + + return (sb.toString()); + } + + /** + * Makes a deep copy of a List containing preexists information. + */ + private static HashMap clonePreexists(final HashMap old) { + final HashMap clone = new HashMap(); + final Iterator vars = old.keySet().iterator(); + while (vars.hasNext()) { + final LocalVariable var = (LocalVariable) vars.next(); + final HashSet set = (HashSet) old.get(var); + if (set == null) { + clone.put(var, null); + } else { + clone.put(var, set.clone()); + } + } + return (clone); + } + + public void visit_nop(final Instruction inst) { + } + + public void visit_ldc(final Instruction inst) { + push(inst); + } + + public void visit_iload(final Instruction inst) { + push(inst); + } + + public void visit_lload(final Instruction inst) { + push(inst); + } + + public void visit_fload(final Instruction inst) { + push(inst); + } + + public void visit_dload(final Instruction inst) { + push(inst); + } + + public void visit_aload(final Instruction inst) { + push(inst); + } + + public void visit_iaload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_laload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_faload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_daload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_aaload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_baload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_caload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_saload(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_istore(final Instruction inst) { + pop(); + } + + public void visit_lstore(final Instruction inst) { + pop(); + } + + public void visit_fstore(final Instruction inst) { + pop(); + } + + public void visit_dstore(final Instruction inst) { + pop(); + } + + public void visit_astore(final Instruction inst) { + // When we store an object to a local variable, we need to keep + // track of whether or not the object being stored (and hence the + // variable into which it is stored) is preexistent. + final LocalVariable var = (LocalVariable) inst.operand(); + final HashSet set = preexistsAtDepth(0); + + if (set == null) { + InstructionStack.pre(" " + var + " does not preexist"); + this.currPreexists.put(var, null); + + } else if (set.isEmpty()) { + InstructionStack.pre(" " + var + " preexists"); + this.currPreexists.put(var, new HashSet()); + + } else { + // This store superceeds anything else that was already in this + // local, so don't merge the sets. + InstructionStack.pre(" " + var + " preexists with types"); + this.currPreexists.put(var, set.clone()); + } + + pop(); + } + + public void visit_iastore(final Instruction inst) { + pop(3); + } + + public void visit_lastore(final Instruction inst) { + pop(3); + } + + public void visit_fastore(final Instruction inst) { + pop(3); + } + + public void visit_dastore(final Instruction inst) { + pop(3); + } + + public void visit_aastore(final Instruction inst) { + pop(3); + } + + public void visit_bastore(final Instruction inst) { + pop(3); + } + + public void visit_castore(final Instruction inst) { + pop(3); + } + + public void visit_sastore(final Instruction inst) { + pop(3); + } + + /** + * Helper method for asserting that all of the instructions are of a certain + * category. + */ + private void checkCategory(final Set insts, final int category) { + final Iterator iter = insts.iterator(); + while (iter.hasNext()) { + final Instruction inst = (Instruction) iter.next(); + Assert.isTrue(inst.category() == category, "Category mismatch: " + + inst.category() + " != " + category); + } + } + + /** + * Helper method for asserting that all of the instructions have the same + * category. The category is returned. + */ + private int checkCategory(final Set insts) { + int category = 0; + final Iterator iter = insts.iterator(); + while (iter.hasNext()) { + final Instruction inst = (Instruction) iter.next(); + if (category == 0) { + category = inst.category(); + + } else { + Assert.isTrue(inst.category() == category, + "Category mismatch in instruction set"); + } + } + + Assert.isTrue(category != 0, "No instructions in set"); + return (category); + } + + public void visit_pop(final Instruction inst) { + final Set insts = atDepth(0); + + checkCategory(insts, 1); + + pop(); + } + + public void visit_pop2(final Instruction inst) { + // Form 1 pops two category 1 values off the stack. Form 2 pops + // one category 2 value off the stack. + + final Set top1 = (Set) currStack.removeLast(); + + final int category = checkCategory(top1); + + if (category == 1) { + // Pop another category 1 off + final Set top2 = (Set) currStack.removeLast(); + checkCategory(top2, 1); + } + } + + public void visit_dup(final Instruction inst) { + // Duplicate the category 1 value on the top of the stack + final Set dup = atDepth(0); + + checkCategory(dup, 1); + + currStack.add(new HashSet(dup)); + } + + public void visit_dup_x1(final Instruction inst) { + final Set dup = atDepth(0); + + checkCategory(dup, 1); + + currStack.add(currStack.size() - 2, new HashSet(dup)); + } + + public void visit_dup_x2(final Instruction inst) { + // Top value on stack must be category 1. + final Set top1 = atDepth(0); + + checkCategory(top1, 1); + + final Set top2 = atDepth(1); + + final int category = checkCategory(top2); + + if (category == 1) { + final Set top3 = atDepth(2); + checkCategory(top3, 1); + + // Form 1: Dup top value and put three down + + currStack.add(currStack.size() - 3, new HashSet(top1)); + + } else { + // Form 2: Dup top value and put two down + + currStack.add(currStack.size() - 2, new HashSet(top1)); + } + } + + public void visit_dup2(final Instruction inst) { + // If top two values are both category 1, dup them both. + // Otherwise, dup the one category 2 value. + final Set top = atDepth(0); + + final int category = checkCategory(top); + + if (category == 1) { + final Set top1 = atDepth(1); + checkCategory(top1, 1); + + // Form 1: Dup top two values + + currStack.add(new HashSet(top1)); + currStack.add(new HashSet(top)); + + } else { + // Form 2: Dup top value + + currStack.add(new HashSet(top)); + } + } + + public void visit_dup2_x1(final Instruction inst) { + // If the top two values are of category 1, then dup them and put + // them three down. Otherwise, the top two values are of category + // 2 and the top value is put two down. + final Set top = atDepth(0); + + final int category = checkCategory(top); + + if (category == 1) { + final Set top1 = atDepth(1); + checkCategory(top1, 1); + + // Form 1: Dup top two values and put three down + + final int n = currStack.size() - 3; + currStack.add(n, top1); + currStack.add(n, top); + + } else { + final Set top1 = atDepth(1); + checkCategory(top1, 1); + + // Form 2: Dup top value and put two down + + currStack.add(currStack.size() - 2, new HashSet(top)); + } + } + + public void visit_dup2_x2(final Instruction inst) { + // If the two four values are all category 1, then duplicate the + // top two values and put them four down. If the top value is of + // category 2 and the next two are of type 1, then dup the top + // value and put it three down. If the top two values are both + // category 1 and the third value is type 2, then dup the top two + // values and put them three down. If the top two values are both + // category 2, then dup the top one and put it two down. + + final Set top = atDepth(0); + final int category = checkCategory(top); + + if (category == 1) { + final Set top1 = atDepth(1); + final int category1 = checkCategory(top1); + if (category1 == 1) { + final Set top2 = atDepth(2); + final int category2 = checkCategory(top2); + if (category2 == 1) { + checkCategory(atDepth(3), 1); + + // Form 1: Dup top two values and put four down + final int n = currStack.size() - 4; + currStack.add(n, new HashSet(top1)); + currStack.add(n, new HashSet(top)); + + } else { + // Form 3: Dup top two values and put three down + final int n = currStack.size() - 3; + currStack.add(n, new HashSet(top1)); + currStack.add(n, new HashSet(top)); + + } + + } else { + Assert.isTrue(false, "Impossible stack combination for " + + "dup2_x1: ... 2 1"); + } + + } else { + final Set top1 = atDepth(1); + final int category1 = checkCategory(top1); + if (category1 == 1) { + final int category2 = checkCategory(atDepth(2)); + if (category2 == 1) { + // Form 2: Dup top value and put three down + currStack.add(currStack.size() - 3, new HashSet(top)); + + } else { + Assert.isTrue(false, "Impossible stack combination for " + + "dup2_x1: ... 2 1 2"); + } + + } else { + // Form 4: Dup top and put two down + currStack.add(currStack.size() - 2, new HashSet(top)); + } + } + } + + public void visit_swap(final Instruction inst) { + final Set top = (Set) currStack.removeLast(); + final Set next = (Set) currStack.removeLast(); + + checkCategory(top, 1); + checkCategory(next, 1); + + currStack.add(top); + currStack.add(next); + } + + public void visit_iadd(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ladd(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fadd(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_dadd(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_isub(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lsub(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fsub(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_dsub(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_imul(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lmul(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fmul(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_dmul(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_idiv(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ldiv(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fdiv(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ddiv(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_irem(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lrem(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_frem(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_drem(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ineg(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_lneg(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_fneg(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_dneg(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_ishl(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lshl(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ishr(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lshr(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_iushr(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lushr(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_iand(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_land(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ior(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lor(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ixor(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_lxor(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_iinc(final Instruction inst) { + // Kind of a fine point here. The instruction doesn't change the + // stack, iinc increments a local variable. + } + + public void visit_i2l(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_i2f(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_i2d(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_l2i(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_l2f(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_l2d(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_f2i(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_f2l(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_f2d(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_d2i(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_d2l(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_d2f(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_i2b(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_i2c(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_i2s(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_lcmp(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fcmpl(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_fcmpg(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_dcmpl(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_dcmpg(final Instruction inst) { + pop(2); + push(inst); + } + + public void visit_ifeq(final Instruction inst) { + pop(); + } + + public void visit_ifne(final Instruction inst) { + pop(); + } + + public void visit_iflt(final Instruction inst) { + pop(); + } + + public void visit_ifge(final Instruction inst) { + pop(); + } + + public void visit_ifgt(final Instruction inst) { + pop(); + } + + public void visit_ifle(final Instruction inst) { + pop(); + } + + public void visit_if_icmpeq(final Instruction inst) { + pop(2); + } + + public void visit_if_icmpne(final Instruction inst) { + pop(2); + } + + public void visit_if_icmplt(final Instruction inst) { + pop(2); + } + + public void visit_if_icmpge(final Instruction inst) { + pop(2); + } + + public void visit_if_icmpgt(final Instruction inst) { + pop(2); + } + + public void visit_if_icmple(final Instruction inst) { + pop(2); + } + + public void visit_if_acmpeq(final Instruction inst) { + pop(2); + } + + public void visit_if_acmpne(final Instruction inst) { + pop(2); + } + + public void visit_goto(final Instruction inst) { + // Nothing to do + } + + public void visit_jsr(final Instruction inst) { + push(inst); + } + + public void visit_ret(final Instruction inst) { + // Nothing to do + } + + public void visit_switch(final Instruction inst) { + pop(); + } + + // Return stuff performed by handle(Instruction) + public void visit_ireturn(final Instruction inst) { + + } + + public void visit_lreturn(final Instruction inst) { + + } + + public void visit_freturn(final Instruction inst) { + + } + + public void visit_dreturn(final Instruction inst) { + + } + + public void visit_areturn(final Instruction inst) { + + } + + public void visit_return(final Instruction inst) { + + } + + public void visit_getstatic(final Instruction inst) { + push(inst); + } + + public void visit_putstatic(final Instruction inst) { + pop(); + } + + public void visit_putstatic_nowb(final Instruction inst) { + pop(); + } + + public void visit_getfield(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_putfield(final Instruction inst) { + pop(2); + } + + public void visit_putfield_nowb(final Instruction inst) { + pop(2); + } + + public void visit_invokevirtual(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + pop(type.paramTypes().length); + pop(); // Pop receiver + + if (type.returnType() != Type.VOID) { + push(inst); + } + } + + public void visit_invokespecial(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + pop(type.paramTypes().length); + pop(); // Pop receiver + + if (type.returnType() != Type.VOID) { + push(inst); + } + } + + public void visit_invokestatic(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + pop(type.paramTypes().length); + + if (type.returnType() != Type.VOID) { + push(inst); + } + } + + public void visit_invokeinterface(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + pop(type.paramTypes().length); + pop(); // Pop receiver + + if (type.returnType() != Type.VOID) { + push(inst); + } + } + + public void visit_new(final Instruction inst) { + push(inst); + } + + public void visit_newarray(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_arraylength(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_athrow(final Instruction inst) { + // I guess... + pop(); + push(inst); + } + + public void visit_checkcast(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_instanceof(final Instruction inst) { + pop(); + push(inst); + } + + public void visit_monitorenter(final Instruction inst) { + pop(); + } + + public void visit_monitorexit(final Instruction inst) { + pop(); + } + + public void visit_multianewarray(final Instruction inst) { + final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); + final int dim = operand.dimensions(); + + pop(dim); + + push(inst); + } + + public void visit_ifnull(final Instruction inst) { + pop(); + } + + public void visit_ifnonnull(final Instruction inst) { + pop(); + } + + public void visit_rc(final Instruction inst) { + } + + public void visit_aupdate(final Instruction inst) { + } + + public void visit_supdate(final Instruction inst) { + } + + public void visit_aswizzle(final Instruction inst) { + } + + public void visit_aswrange(final Instruction inst) { + } + +} diff --git a/src/EDU/purdue/cs/bloat/inline/Makefile b/src/EDU/purdue/cs/bloat/inline/Makefile new file mode 100644 index 0000000..93e3e9d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/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. +# +# 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 + + +CLASS = \ + CallGraph.class\ + Inline.class\ + InlineContext.class\ + InlineStats.class\ + InstructionStack.class\ + Specialize.class\ + StackHeightCounter.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/inline/Specialize.java b/src/EDU/purdue/cs/bloat/inline/Specialize.java new file mode 100644 index 0000000..6e562dc --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/Specialize.java @@ -0,0 +1,666 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Performs call site specialization for virtual method invocations. At each + * virtual method invocation, the call graph is consulted to determine which + * methods the call site could resolve to. The virtual call site is converted + * into a "switch" on the method receiver. Each "case" corresponds to a possible + * type that the receiver may take on. Inside the "case" branch, the receiver is + * cast to the expected type. Finally, a static version of the virtual method is + * called. In the "default" case, the virtual method is invoked. + */ +public class Specialize implements Opcode { + public static boolean DEBUG = false; + + public static boolean DB_STATIC = false; + + public static boolean PRINTCODE = false; + + public static boolean STATS = false; + + public static boolean NOSTATIC = false; + + public static boolean USE_PREEXISTS = true; + + private final Map staticMethods = new HashMap(); + + private final Set specialized = new HashSet(); // calls that have + // specialized + + private InlineStats stats; + + /** + * Maximum number of specializations per call site + */ + public static int MAX_MORPH = 5; + + /** + * Do we specialize monomorphic call sites? By default, false. + */ + public static boolean MONO = false; + + private InlineContext context; + + private static void db(final String s) { + if (Specialize.DEBUG) { + System.out.println(s); + } + } + + /** + * Constructor. + */ + public Specialize(final InlineContext context) { + this.context = context; + + stats = context.getInlineStats(); + } + + /** + * Examines the virtual method call sites in a method and specializes them + * based on the resolves-to information found in the call graph. + * + * @return true, if the method was modified (that is, it + * requires committing) + */ + public boolean specialize(final MethodEditor method) { + if (method.isNative()) { + // Can't do anything with native methods + return (false); + } + + // Do we ignore this method + if (context.ignoreMethod(method.memberRef())) { + return (false); + } + + Specialize.db("\nSpecializing " + method.declaringClass().name() + "." + + method.name() + method.type()); + + // Okay, what are we doing here? The first task is to find + // virtual call sites. That's easy. Finding the code that pushes + // the call's arguments is not so easy. We use the following + // method. At each instruction we use a InstructionStack to keep + // track of what the stack looks like. That is, we want to know + // which instructions push which stack elements. Thus, at a call + // site we can easily determine which instruction(s) (yes, there + // might be more than one) push the receiver object on the stack. + // We insert a dup and a store into a local variable after these + // instructions. Back at the call site we load from the local + // variable and specialize away. This method only adds a couple + // of instructions and can handle all sort of bizzare control + // flow. + + final CallGraph cg = context.getCallGraph(); + boolean changed = false; + final InstructionStack stack = new InstructionStack(method); + + // Go through the code looking for call sites. Along the way keep + // track of the stack. + final List code = method.code(); + + for (int i = 0; i < code.size(); i++) { + final Object o = code.get(i); + if (o instanceof Instruction) { + final Instruction inst = (Instruction) o; + + if ((inst.opcodeClass() == Opcode.opcx_invokevirtual) + || (inst.opcodeClass() == Opcode.opcx_invokeinterface)) { + // Found a virtual method call + Specialize.db(" Call: " + inst); + + final MemberRef callee = (MemberRef) inst.operand(); + + // Do we ignore the callee? + if (context.ignoreMethod(callee)) { + Specialize.db(" Explicitly ignoring this method"); + stack.handle(inst); + continue; + } + + MethodEditor me = null; + try { + me = context.editMethod(callee); + + } catch (final NoSuchMethodException ex0) { + System.err.println("** Cannot find method " + callee); + ex0.printStackTrace(System.err); + System.exit(1); + } + + if (me.isFinal() && !me.isNative()) { + // Ah, we have a final method. Just convert it to a + // static method. + Specialize.db(" Converting final method " + callee + + " to static"); + + final int index = code.indexOf(inst); + final Instruction newInst = new Instruction( + Opcode.opcx_invokespecial, me.memberRef()); + code.add(index, newInst); + code.remove(inst); + stack.handle(newInst); + + // Make note of call + continue; + } + + // Calculate the number of stack slots needed to hold the + // callee's arguments. + final int nArgs = callee.nameAndType().type().paramTypes().length; + + // Determine the instructions that push the receiver on the + // stack. Make note of these. + final Set pushes = stack.atDepth(nArgs); + if (Specialize.DEBUG) { + Specialize + .db(" Receiver on stack after: " + + (stack.preexistsAtDepth(nArgs) != null ? " (preexists)" + : "")); + final Iterator pushies = pushes.iterator(); + while (pushies.hasNext()) { + final Instruction push = (Instruction) pushies + .next(); + Specialize.db(" " + code.indexOf(push) + ") " + + push); + } + } + + Set set = null; // Method call could resolve to + if (Specialize.USE_PREEXISTS) { + // If the receiver is not preexistent, then we can't + // safely inline the method. Don't bother converting it + // into a static method. + final Set preexists = stack.preexistsAtDepth(nArgs); + if (preexists == null) { + stack.handle(inst); + if (Specialize.STATS) { + stats.noteNoPreexist(); + } + Specialize.db(" Receiver does not preexist"); + continue; + } + + // If the preexists is non-empty, then it contains the + // type(s) that we know the receiver can take on. Remove + // all others from the set of possible receiver types. + // Wicked awesome! + if (!preexists.isEmpty()) { + set = cg.resolvesTo(callee, preexists); + ; + + if (Specialize.DEBUG) { + final StringBuffer sb = new StringBuffer( + " retaining: "); + final Iterator iter = preexists.iterator(); + while (iter.hasNext()) { + final Type type = (Type) iter.next(); + sb.append(Type.truncatedName(type)); + if (iter.hasNext()) { + sb.append(", "); + } + } + Specialize.db(sb.toString()); + } + } + } + + if (set == null) { + set = cg.resolvesTo(callee); + } + + if ((set == null) || set.isEmpty() + || specialized.contains(inst)) { + // What does it mean if a call has no call sites? Well, + // it means that no class that implements the method is + // created. So, either we have a null pointer exception + // waiting to happen or the class is instantiated in + // someplace that we don't analyze (e.g. native + // methods). + Specialize + .db(" " + + (specialized.contains(inst) ? "Call already handled" + : "Resolves to no methods") + + ". Ignoring."); + + // Don't forget to update the stack + stack.handle(inst); + continue; + } + + specialized.add(inst); + + if (Specialize.STATS) { + // Make note of the number of methods a call site + // resolves + // to + stats.noteMorphicity(set.size()); + } + + if (set.size() > Specialize.MAX_MORPH) { + // If we only care about monomorphic calls sites, then + // go + // home. + stack.handle(inst); + continue; + } + + // Don't update the stack if the call is specialized. This + // will all get taken care of later. + + // Okay, it's showtime. Originally, I specialized all of + // the call sites after I had visited the entire method. + // However, this was incorrect because specialized code may + // push the receiver on the stack. Bummer. + + // We're making changes + changed = true; + + // Add a dup and store right after the instructions that + // pushes the receiver. + final LocalVariable var = method.newLocal(callee + .declaringClass()); + + Specialize.db(" New local: " + var + " " + var.type() + + " (from method " + method.name() + method.type() + + ", max " + method.maxLocals() + ")"); + + final Iterator iter = pushes.iterator(); + Assert.isTrue(iter.hasNext(), + "No instruction pushes receiver for " + inst); + Instruction newInst = null; + + boolean mono = (set.size() == 1); + + while (!mono && iter.hasNext()) { + // Since we've already examined the code that pushes the + // receiver, we don't need to worry about keeping track + // of + // the stack height. Besides, this code doesn't effect + // the stack. I hope. + + // There is no need to dup the receiver object if the + // call + // is monomorphic. This avoids extra dups being + // executed. + + final Instruction push = (Instruction) iter.next(); + final int index = code.indexOf(push); + Assert.isTrue(index != -1, push + " not found in code"); + newInst = new Instruction(Opcode.opcx_dup); + code.add(index + 1, newInst); + + final Instruction store = new Instruction( + Opcode.opcx_astore, var); + code.add(index + 2, store); + } + + // We've added instructions before the call. This will + // mess up our code index, i. Update accordingly. + i = code.indexOf(inst) - 1; + + // Now specialize the call site. Examine each method the + // call could resolve to in an order such that overriding + // methods come before overriden methods. We don't need to + // keep track of the stack because these instructions will + // be iterated over in a minute. I hope. + + Specialize.db(" Specializing call site..."); + + Label nextLabel = null; + final Label endLabel = method.newLabel(); + endLabel.setStartsBlock(true); + endLabel.setComment("End Specialization"); + + int index = code.indexOf(inst); + Assert.isTrue(index != -1, "Call " + inst + + " not found in code"); + + index--; // Trust me + + Assert.isTrue(set != null, "Call to " + callee + + " should resolve to something"); + final Object[] sortedSites = set.toArray(); + + if (sortedSites.length == 1) { + // If the call site only resolve to one method, then we + // don't need to do all of the specialization stuff. + // Just + // call the static-ized method. + Specialize.db(" Monomorphic call site"); + final MemberRef resolvesTo = (MemberRef) sortedSites[0]; + MethodEditor resolvesToMethod = null; + try { + resolvesToMethod = this.context + .editMethod(resolvesTo); + + } catch (final NoSuchMethodException ex) { + System.err.println("** No such method " + + resolvesTo); + System.exit(1); + } + + if (resolvesToMethod.isNative()) { + // Can't specialize native methods. Oh well. + // Remember + // that it is possible for an overriding method to + // be + // native. + newInst = new Instruction( + Opcode.opcx_invokespecial, resolvesTo); + code.add(++index, newInst); + + } else { + // Make a static version of the virtual method being + // invoked. Call it. + if (Specialize.NOSTATIC) { + // For testing the specialization stuff, call + // the + // virtual method instead of the static method. + newInst = new Instruction(inst.opcodeClass(), + inst.operand()); + specialized.add(newInst); + + } else { + newInst = new Instruction( + Opcode.opcx_invokespecial, + resolvesToMethod.memberRef()); + } + + code.add(++index, newInst); + } + + // Remove the call to the virtual method + code.remove(inst); + continue; + } + + // If we get to here we have a polymorphic call site + + for (int s = 0; (s < sortedSites.length) + && (s <= Specialize.MAX_MORPH); s++) { + final MemberRef resolvesTo = (MemberRef) sortedSites[s]; + + // Do we ignore this method? + if (context.ignoreMethod(resolvesTo)) { + continue; + } + + Specialize.db(" Resolves to " + resolvesTo + ")"); + + final Type rType = resolvesTo.declaringClass(); + + // This may be the target of a branch + if (nextLabel != null) { + nextLabel.setComment("Type " + rType); + code.add(++index, nextLabel); + } + + // Push the receiver object + newInst = new Instruction(Opcode.opcx_aload, var); + code.add(++index, newInst); + + // Check to see if it is this type + newInst = new Instruction(Opcode.opcx_instanceof, rType); + code.add(++index, newInst); + + // If its not, try something else + nextLabel = method.newLabel(); + nextLabel.setStartsBlock(true); + newInst = new Instruction(Opcode.opcx_ifeq, nextLabel); + code.add(++index, newInst); + + // We have to add a label here because all branches must + // be followed by a label. If we don't BLOAT will barf + // during CFG construction. And that's messy. + final Label grumble = method.newLabel(); + grumble.setStartsBlock(true); + code.add(++index, grumble); + + // Otherwise, call the static-ified method. We can't + // cast + // the receiver to the expected type. Oh well, we + // weren't + // planning on verifying anyway. + MethodEditor resolvesToMethod = null; + try { + resolvesToMethod = this.context + .editMethod(resolvesTo); + + } catch (final NoSuchMethodException ex) { + System.err.println("** No such method " + + resolvesTo); + System.exit(1); + } + + if (resolvesToMethod.isNative()) { + // Can't specialize native methods. Oh well. + // Remember + // that it is possible for an overriding method to + // be + // native. + newInst = new Instruction( + Opcode.opcx_invokespecial, resolvesTo); + code.add(++index, newInst); + + } else { + // Make a static version of the virtual method being + // invoked. Call it. + if (Specialize.NOSTATIC) { + // For testing the specialization stuff, call + // the + // virtual method instead of the static method. + newInst = new Instruction(inst.opcodeClass(), + inst.operand()); + specialized.add(newInst); + + } else { + newInst = new Instruction( + Opcode.opcx_invokespecial, + resolvesToMethod.memberRef()); + } + + code.add(++index, newInst); + } + + // Jump to the end + newInst = new Instruction(Opcode.opcx_goto, endLabel); + code.add(++index, newInst); + } + + // Default code still invokes virtual method + if (nextLabel != null) { + nextLabel.setComment("Default invocation"); + code.add(++index, nextLabel); + } + + // Call virtual method. Should be next in line. + index++; + + // Jump to end. + newInst = new Instruction(Opcode.opcx_goto, endLabel); + code.add(++index, newInst); + + // We're all done + code.add(++index, endLabel); + + if (Specialize.DEBUG) { + // Print code up to and including end label + System.out.println(" Code after specializing " + + callee.name() + callee.type()); + for (int j = 0; (j <= (index + 2)) && (j < code.size()); j++) { + final Object q = code.get(j); + if (q instanceof Label) { + final Label label = (Label) q; + System.out + .println(" " + + j + + ") " + + label + + (label.startsBlock() ? " (starts block)" + : "")); + + } else { + System.out.println(" " + j + ") " + q); + } + } + } + + // Don't print the stack at the call instruction + continue; + + } else if (inst.opcodeClass() == Opcode.opcx_invokespecial) { + // To make our lives easier, convert some invokespecial to + // invoke static. Basically, if the callee is non-native + // and is a method of the caller's class or one of its + // superclasses, we can convert it. + final MemberRef callee = (MemberRef) inst.operand(); + + MethodEditor me = null; + try { + me = context.editMethod(callee); + if (me.isNative() || me.isSynchronized() + || me.isConstructor()) { + // Forget about these guys + stack.handle(inst); + + } else { + final Type calleeType = callee.declaringClass(); + final Type callerType = method.declaringClass() + .type(); + final ClassHierarchy hier = context.getHierarchy(); + + if (calleeType.equals(callerType) + || hier.subclassOf(callerType, calleeType)) { + Specialize.db(" Making special " + inst + + " static"); + + final int index = code.indexOf(inst); + final Instruction newInst = new Instruction( + Opcode.opcx_invokespecial, me + .memberRef()); + code.add(index, newInst); + code.remove(inst); + stack.handle(newInst); + + } else { + // Don't forget about the stack + stack.handle(inst); + } + } + + } catch (final NoSuchMethodException ex2) { + System.err.println("** Couldn't find method " + callee); + ex2.printStackTrace(System.err); + System.exit(1); + } + + } else { + // Just update stack + stack.handle(inst); + } + + if (Specialize.DEBUG) { + Specialize.db(" " + code.indexOf(inst) + ") " + inst); + for (int q = 0; q < stack.height(); q++) { + final Iterator iter = stack.atDepth(q).iterator(); + if (iter.hasNext()) { + System.out.print(" "); + } + while (iter.hasNext()) { + System.out.print(code.indexOf(iter.next())); + if (iter.hasNext()) { + System.out.print(", "); + } + } + + if (stack.preexistsAtDepth(q) != null) { + System.out.print(" (preexists)"); + + } else { + System.out.print(" (does not preexist)"); + } + System.out.println(""); + } + } + + } else if (o instanceof Label) { + // We've reached a label. + final Label label = (Label) o; + stack.handle(label); + + Specialize.db(" " + o); + + if (Specialize.DEBUG) { + for (int q = 0; q < stack.height(); q++) { + final Iterator iter = stack.atDepth(q).iterator(); + if (iter.hasNext()) { + System.out.print(" "); + } + while (iter.hasNext()) { + System.out.print(code.indexOf(iter.next())); + if (iter.hasNext()) { + System.out.print(", "); + } + } + System.out.println(""); + } + } + + } else { + Assert.isTrue(false, "What is " + o + + " doing in the instruction stream?"); + } + } + + if (Specialize.DEBUG || Specialize.PRINTCODE) { + // Print out the code + System.out.println("Specialized code for " + + method.declaringClass().name() + "." + method.name() + + method.type()); + + final Iterator iter2 = code.iterator(); + while (iter2.hasNext()) { + final Object o = iter2.next(); + + if (o instanceof Label) { + System.out.println(""); + } + + System.out.println(" " + o); + } + } + + if (changed) { + method.setDirty(true); + } + + return (changed); + } + +} 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..c7f59ca --- /dev/null +++ b/src/EDU/purdue/cs/bloat/inline/StackHeightCounter.java @@ -0,0 +1,726 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.inline; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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 HashMap labelHeights; // Maps labels to their heights as Integers + + private MethodEditor method; // Method whose height we're computing + + Set tryCatches; // TryCatches active at current instruction + + private static void db(final String s) { + if (StackHeightCounter.DEBUG) { + System.out.println(s); + } + } + + public StackHeightCounter(final MethodEditor method) { + this.method = method; + this.height = 0; + this.labelHeights = new HashMap(); + this.tryCatches = new HashSet(); + } + + /** + * 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(final Label label) { + final Integer labelHeight = (Integer) labelHeights.get(label); + 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. + final Iterator tryCatches = method.tryCatches().iterator(); + while (tryCatches.hasNext()) { + final TryCatch tc = (TryCatch) tryCatches.next(); + if (tc.handler().equals(label)) { + label.setStartsBlock(true); + height = 1; + break; + } + + if (tc.start().equals(label)) { + // If this block starts a protected region make note of the + // TryCatch block + this.tryCatches.add(tc); + } + + if (tc.end().equals(label)) { + // If this block ends a protected region, remove it from the + // tryCatches list + this.tryCatches.remove(tc); + } + } + } + + /** + * Handles an instruction. Special provisions must be made to handle jumps, + * switches, throws, and returns. + */ + public void handle(final Instruction inst) { + inst.visit(this); + + if (inst.isJump()) { + final Label target = (Label) inst.operand(); + target.setStartsBlock(true); + final Integer targetHeight = (Integer) labelHeights.get(target); + if (targetHeight != null) { + if (targetHeight.intValue() != height) { + // Make sure stack heights match + StackHeightCounter.db("Stack height mismatch (" + + targetHeight.intValue() + " != " + height + + ") at " + inst); + } + + } else { + labelHeights.put(target, new Integer(height)); + } + + } else if (inst.isSwitch()) { + // Propagate height to all targets + final Switch sw = (Switch) inst.operand(); + final Label defaultTarget = sw.defaultTarget(); + defaultTarget.setStartsBlock(true); + final Integer dTargetHeight = (Integer) labelHeights + .get(defaultTarget); + if (dTargetHeight != null) { + if (dTargetHeight.intValue() != height) { + // Make sure stack heights match + StackHeightCounter.db("Stack height mismatch (" + + dTargetHeight.intValue() + " != " + height + + ") at " + inst); + } + } else { + labelHeights.put(defaultTarget, new Integer(height)); + } + + final Label[] targets = sw.targets(); + for (int t = 0; t < targets.length; t++) { + final Label target = targets[t]; + target.setStartsBlock(true); + final Integer targetHeight = (Integer) labelHeights.get(target); + if (targetHeight != null) { + if (targetHeight.intValue() != height) { + // Make sure stack heights match + StackHeightCounter.db("Stack height mismatch (" + + targetHeight.intValue() + " != " + height + + ") at " + inst); + } + } else { + labelHeights.put(target, new Integer(height)); + } + } + + } else if (inst.isJsr()) { + // 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? + final Label subroutine = (Label) inst.operand(); + subroutine.setStartsBlock(true); + final Integer subHeight = (Integer) labelHeights.get(subroutine); + if (subHeight != null) { + if (subHeight.intValue() != height + 1) { + StackHeightCounter + .db("Stack height mismatch at subroutine (" + + subHeight.intValue() + " != " + + (height + 1) + ") at " + inst); + } + + } else { + labelHeights.put(subroutine, new Integer(height + 1)); + } + + } else if (inst.isThrow() || inst.isReturn()) { + // Clear the stack + height = 0; + } + } + + /** + * Simulates the effect of "backing up" over an instruction. + */ + public void unhandle(final Instruction 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_ldc(final Instruction inst) { + final Object operand = inst.operand(); + + if ((operand instanceof Long) || (operand instanceof Double)) { + height += 2; + + } else { + height += 1; + } + } + + public void visit_iload(final Instruction inst) { + height += 1; + } + + public void visit_lload(final Instruction inst) { + height += 2; + } + + public void visit_fload(final Instruction inst) { + height += 1; + } + + public void visit_dload(final Instruction inst) { + height += 2; + } + + public void visit_aload(final Instruction inst) { + height += 1; + } + + public void visit_iaload(final Instruction inst) { + height -= 1; + } + + public void visit_laload(final Instruction inst) { + height -= 0; + } + + public void visit_faload(final Instruction inst) { + height -= 1; + } + + public void visit_daload(final Instruction inst) { + height -= 0; + } + + public void visit_aaload(final Instruction inst) { + height -= 1; + } + + public void visit_baload(final Instruction inst) { + height -= 1; + } + + public void visit_caload(final Instruction inst) { + height -= 1; + } + + public void visit_saload(final Instruction inst) { + height -= 1; + } + + public void visit_istore(final Instruction inst) { + height -= 1; + } + + public void visit_lstore(final Instruction inst) { + height -= 2; + } + + public void visit_fstore(final Instruction inst) { + height -= 1; + } + + public void visit_dstore(final Instruction inst) { + height -= 2; + } + + public void visit_astore(final Instruction inst) { + height -= 1; + } + + public void visit_iastore(final Instruction inst) { + height -= 3; + } + + public void visit_lastore(final Instruction inst) { + height -= 4; + } + + public void visit_fastore(final Instruction inst) { + height -= 3; + } + + public void visit_dastore(final Instruction inst) { + height -= 4; + } + + public void visit_aastore(final Instruction inst) { + height -= 3; + } + + public void visit_bastore(final Instruction inst) { + height -= 3; + } + + public void visit_castore(final Instruction inst) { + height -= 3; + } + + public void visit_sastore(final Instruction inst) { + height -= 3; + } + + public void visit_pop(final Instruction inst) { + height -= 1; + } + + public void visit_pop2(final Instruction inst) { + height -= 2; + } + + public void visit_dup(final Instruction inst) { + height += 1; + } + + public void visit_dup_x1(final Instruction inst) { + height += 1; + } + + public void visit_dup_x2(final Instruction inst) { + height += 1; + } + + public void visit_dup2(final Instruction inst) { + height += 2; + } + + public void visit_dup2_x1(final Instruction inst) { + height += 2; + } + + public void visit_dup2_x2(final Instruction inst) { + height += 2; + } + + public void visit_iadd(final Instruction inst) { + height -= 1; + } + + public void visit_ladd(final Instruction inst) { + height -= 2; + } + + public void visit_fadd(final Instruction inst) { + height -= 1; + } + + public void visit_dadd(final Instruction inst) { + height -= 2; + } + + public void visit_isub(final Instruction inst) { + height -= 1; + } + + public void visit_lsub(final Instruction inst) { + height -= 2; + } + + public void visit_fsub(final Instruction inst) { + height -= 1; + } + + public void visit_dsub(final Instruction inst) { + height -= 2; + } + + public void visit_imul(final Instruction inst) { + height -= 1; + } + + public void visit_lmul(final Instruction inst) { + height -= 2; + } + + public void visit_fmul(final Instruction inst) { + height -= 1; + } + + public void visit_dmul(final Instruction inst) { + height -= 2; + } + + public void visit_idiv(final Instruction inst) { + height -= 1; + } + + public void visit_ldiv(final Instruction inst) { + height -= 2; + } + + public void visit_fdiv(final Instruction inst) { + height -= 1; + } + + public void visit_ddiv(final Instruction inst) { + height -= 2; + } + + public void visit_irem(final Instruction inst) { + height -= 1; + } + + public void visit_lrem(final Instruction inst) { + height -= 2; + } + + public void visit_frem(final Instruction inst) { + height -= 1; + } + + public void visit_drem(final Instruction inst) { + height -= 2; + } + + public void visit_ishl(final Instruction inst) { + height -= 1; + } + + public void visit_lshl(final Instruction inst) { + height -= 1; + } + + public void visit_ishr(final Instruction inst) { + height -= 1; + } + + public void visit_lshr(final Instruction inst) { + height -= 1; + } + + public void visit_iushr(final Instruction inst) { + height -= 1; + } + + public void visit_lushr(final 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_iand(final Instruction inst) { + height -= 1; + } + + public void visit_land(final Instruction inst) { + height -= 2; + } + + public void visit_ior(final Instruction inst) { + height -= 1; + } + + public void visit_lor(final Instruction inst) { + height -= 2; + } + + public void visit_ixor(final Instruction inst) { + height -= 1; + } + + public void visit_lxor(final Instruction inst) { + height -= 2; + } + + public void visit_i2l(final Instruction inst) { + height += 1; + } + + public void visit_i2d(final Instruction inst) { + height += 1; + } + + public void visit_l2i(final Instruction inst) { + height -= 1; + } + + public void visit_l2f(final Instruction inst) { + height -= 1; + } + + public void visit_f2l(final Instruction inst) { + height += 1; + } + + public void visit_f2d(final Instruction inst) { + height += 1; + } + + public void visit_d2i(final Instruction inst) { + height -= 1; + } + + public void visit_d2f(final Instruction inst) { + height -= 1; + } + + public void visit_lcmp(final Instruction inst) { + height -= 3; + } + + public void visit_fcmpl(final Instruction inst) { + height -= 1; + } + + public void visit_fcmpg(final Instruction inst) { + height -= 1; + } + + public void visit_dcmpl(final Instruction inst) { + height -= 3; + } + + public void visit_dcmpg(final Instruction inst) { + height -= 3; + } + + public void visit_ifeq(final Instruction inst) { + height -= 1; + } + + public void visit_ifne(final Instruction inst) { + height -= 1; + } + + public void visit_iflt(final Instruction inst) { + height -= 1; + } + + public void visit_ifge(final Instruction inst) { + height -= 1; + } + + public void visit_ifgt(final Instruction inst) { + height -= 1; + } + + public void visit_ifle(final Instruction inst) { + height -= 1; + } + + public void visit_if_icmpeq(final Instruction inst) { + height -= 2; + } + + public void visit_if_icmpne(final Instruction inst) { + height -= 2; + } + + public void visit_if_icmplt(final Instruction inst) { + height -= 2; + } + + public void visit_if_icmpge(final Instruction inst) { + height -= 2; + } + + public void visit_if_icmpgt(final Instruction inst) { + height -= 2; + } + + public void visit_if_icmple(final Instruction inst) { + height -= 2; + } + + public void visit_if_acmpeq(final Instruction inst) { + height -= 2; + } + + public void visit_if_acmpne(final Instruction inst) { + height -= 2; + } + + public void visit_jsr(final 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; + } + + public void visit_switch(final Instruction inst) { + height -= 1; + } + + public void visit_ireturn(final Instruction inst) { + height = 0; + } + + public void visit_lreturn(final Instruction inst) { + height = 0; + } + + public void visit_freturn(final Instruction inst) { + height = 0; + } + + public void visit_dreturn(final Instruction inst) { + height = 0; + } + + public void visit_areturn(final Instruction inst) { + height = 0; + } + + public void visit_return(final Instruction inst) { + height = 0; + } + + public void visit_getstatic(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height += type.stackHeight(); + } + + public void visit_putstatic(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height -= type.stackHeight(); + } + + public void visit_putstatic_nowb(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height -= type.stackHeight(); + } + + public void visit_getfield(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height += type.stackHeight() - 1; + } + + public void visit_putfield(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height -= type.stackHeight() + 1; + } + + public void visit_putfield_nowb(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + height -= type.stackHeight() + 1; + } + + public void visit_invokevirtual(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + height += type.returnType().stackHeight() - type.stackHeight() - 1; + } + + public void visit_invokespecial(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + height += type.returnType().stackHeight() - type.stackHeight() - 1; + } + + public void visit_invokestatic(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + height += type.returnType().stackHeight() - type.stackHeight(); + } + + public void visit_invokeinterface(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + height += type.returnType().stackHeight() - type.stackHeight() - 1; + + } + + public void visit_new(final Instruction inst) { + height += 1; + } + + public void visit_monitorenter(final Instruction inst) { + height -= 1; + } + + public void visit_monitorexit(final Instruction inst) { + height -= 1; + } + + public void visit_multianewarray(final Instruction inst) { + final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); + final int dim = operand.dimensions(); + + height += 1 - dim; + } + + public void visit_ifnull(final Instruction inst) { + height -= 1; + } + + public void visit_ifnonnull(final Instruction inst) { + height -= 1; + } + + public void visit_aswizzle(final Instruction inst) { + height -= 2; + } + + public void visit_aswrange(final Instruction inst) { + height -= 3; + } + + /** + * Returns a clone of this StackHeightCounter + */ + public Object clone() { + final StackHeightCounter clone = new StackHeightCounter(this.method); + clone.height = this.height; + clone.labelHeights = (HashMap) this.labelHeights.clone(); + return (clone); + } +} 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/.cvsignore b/src/EDU/purdue/cs/bloat/optimize/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/optimize/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file 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..a86b8dd --- /dev/null +++ b/src/EDU/purdue/cs/bloat/optimize/Main.java @@ -0,0 +1,1155 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.optimize; + +import java.io.*; +import java.text.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.codegen.*; +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.diva.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.inline.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tbaa.*; +import EDU.purdue.cs.bloat.trans.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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 { + // 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; + + public static boolean COMPACT_ARRAY_INIT = true; + + 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 + + /** + * 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(final String[] args) { + try { + Main.loader = new ClassFileLoader(); + + List classes = new ArrayList(args.length); // The classes to + // optimize + boolean gotdir = false; // Has an output directory been specified? + + Main.ARGS = args; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + Main.VERBOSE = true; + Main.loader.setVerbose(true); + + } else if (args[i].equals("-debug")) { + Main.DEBUG = true; + Main.loader.setVerbose(true); + ClassFileLoader.DEBUG = true; + CompactArrayInitializer.DEBUG = true; + ClassEditor.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; + StackPRE.DEBUG = true; + ExprPropagation.DEBUG = true; + DeadCodeElimination.DEBUG = true; + CodeGenerator.DB_OPT_STACK = true; + + } else if (args[i].equals("-trace")) { + Main.TRACE = true; + + } else if (args[i].equals("-db")) { + + if (++i >= args.length) { + System.err.println("** No debugging option specified"); + Main.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]); + Main.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")) { + Main.usage(); + + } else if (args[i].equals("-noanno")) { + Main.ANNO = false; + + } else if (args[i].equals("-anno")) { + Main.ANNO = true; + + } else if (args[i].equals("-print-flow-graph")) { + FlowGraph.PRINT_GRAPH = true; + + } else if (args[i].equals("-preserve-debug")) { + MethodEditor.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")) { + MethodEditor.UNIQUE_HANDLERS = true; + + } else if (args[i].equals("-nocompact-array-init")) { + Main.COMPACT_ARRAY_INIT = false; + + } else if (args[i].equals("-compact-array-init")) { + Main.COMPACT_ARRAY_INIT = true; + + } else if (args[i].equals("-nostack-alloc")) { + Main.STACK_ALLOC = false; + + } else if (args[i].equals("-stack-alloc")) { + Main.STACK_ALLOC = true; + + } else if (args[i].equals("-no-verify")) { + Main.VERIFY = false; + + } else if (args[i].equals("-peel-loops")) { + if (++i >= args.length) { + Main.usage(); + } + + final 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) { + Main.usage(); + } + } catch (final NumberFormatException ex) { + Main.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) { + Main.usage(); + } + + Main.METHOD = args[i]; + + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + Main.loader.setClassPath(classpath); + + } else if (args[i].equals("-classpath/p")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + Main.loader.prependClassPath(classpath); + + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + Main.usage(); + } + + String pkg = args[i]; + + // Account for class file name on command line + if (pkg.endsWith(".class")) { + pkg = pkg.substring(0, pkg.lastIndexOf('.')); + } + + Main.SKIP.add(pkg.replace('.', '/')); + + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + Main.usage(); + } + + String pkg = args[i]; + + // Account for class file name on command line + if (pkg.endsWith(".class")) { + pkg = pkg.substring(0, pkg.lastIndexOf('.')); + } + + Main.ONLY.add(pkg.replace('.', '/')); + + } else if (args[i].equals("-nodce")) { + Main.DCE = false; + + } else if (args[i].equals("-noprop")) { + Main.PROP = false; + + } else if (args[i].equals("-noappre")) { + SSAPRE.NO_ACCESS_PATHS = true; + + } else if (args[i].equals("-nopre")) { + Main.PRE = false; + + } else if (args[i].equals("-dce")) { + Main.DCE = true; + + } else if (args[i].equals("-prop")) { + Main.PROP = true; + + } else if (args[i].equals("-appre")) { + SSAPRE.NO_ACCESS_PATHS = false; + + } else if (args[i].equals("-pre")) { + Main.PRE = true; + + } else if (args[i].equals("-closure")) { + Main.CLOSURE = true; + + } else if (args[i].equals("-opt-stack-1")) { + Main.OPT_STACK_1 = true; + CodeGenerator.OPT_STACK = true; + + } else if (args[i].equals("-opt-stack-2")) { + Main.OPT_STACK_2 = true; + MethodEditor.OPT_STACK_2 = true; + + } else if (args[i].equals("-diva")) { + Main.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")) { + Main.FORCE = true; + + } else if (args[i].startsWith("-")) { + System.err.println("No such option: " + args[i]); + Main.usage(); + + } else if (i == args.length - 1) { + // Last argument is the name of the output directory + + final 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 + Main.loader.setOutputDir(f); + gotdir = true; + } else { + // The argument must be a class name... + classes.add(args[i]); + } + } + + if (!gotdir) { + System.err.println("No output directory specified"); + Main.usage(); + } + + if (classes.size() == 0) { + System.err.println("** No classes specified"); + Main.usage(); + } + + // Use the CachingBloatingContext + Main.context = new CachingBloatContext(Main.loader, classes, + Main.CLOSURE); + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + // Now that we've parsed the command line, load the classes into the + // class loader + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + Main.context.loadClass(name); + + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + if (!Main.CLOSURE) { + final Iterator e = classes.iterator(); + + // Edit only the classes that were specified on the command line + + while (e.hasNext()) { + final String name = (String) e.next(); + Main.editClass(name); + } + } else { + // Edit all the classes in the class file editor and their + // superclasses + + classes = null; + + if (Main.TRACE) { + System.out.println("Computing closure " + + Main.dateFormat.format(new Date())); + } + + final Iterator e = Main.context.getHierarchy().classes() + .iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + Main.editClass(t.className()); + } + } + } + } catch (final 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(final String className) { + ClassFile classFile; // Holds info about a class (implements + // ClassInfo) + + // Get information about the class className + try { + classFile = (ClassFile) Main.context.loadClass(className); + } catch (final ClassNotFoundException ex) { + System.err.println("** Couldn't find class: " + ex.getMessage()); + return; + } + + if (!Main.FORCE) { + // 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. + + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (Main.VERBOSE) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (Main.DEBUG) { + // Print the contents of the class file to System.out + classFile.print(System.out); + } + + final ClassEditor c = Main.context.editClass(classFile); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (Main.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < Main.ONLY.size(); i++) { + final String pkg = (String) Main.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 < Main.SKIP.size(); i++) { + final String pkg = (String) Main.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (Main.VERBOSE) { + System.out.println("Skipping " + c.type().className()); + } + + // We're done with this class file, decrement its reference count + Main.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. + + try { + final File f = classFile.outputFile(); + + if (f.exists()) { + f.delete(); + } + + final File dir = new File(f.getParent()); + dir.mkdirs(); + + if (!dir.exists()) { + throw new RuntimeException("Couldn't create directory: " + dir); + } + + final DataOutputStream out = new DataOutputStream( + new FileOutputStream(f)); + new PrintStream(out).println(); + out.close(); + } catch (final IOException e) { + e.printStackTrace(); + System.exit(1); + } + + if (Main.VERBOSE) { + System.out.println("Optimizing " + c.type().className()); + } + + // Finally, we can start playing with the methods... + final MethodInfo[] methods = c.methods(); + + final int numMethods = methods.length + 1; + ; + int whichMethod = 0; + + for (int j = 0; j < methods.length; j++) { + final MethodEditor m; + + try { + m = Main.context.editMethod(methods[j]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + if (Main.TRACE) { + whichMethod++; + System.out + .println("Optimizing " + name + "." + m.name() + + " (method " + whichMethod + " of " + + numMethods + ")"); + } + + if (Main.METHOD != null) { + // A method name has been specified on the command line using + // -only-method. + boolean pass = true; + + String t = m.name() + m.type(); + + if (t.equals(Main.METHOD)) { + pass = false; + } + + t = m.name(); + + if (t.equals(Main.METHOD)) { + pass = false; + } + + if (pass) { + // This isn't the method we're looking for. + // Decrement its reference count. + Main.context.release(methods[j]); + continue; + } + } + + if (Main.DEBUG) { + m.print(System.out); + } + + if (m.isNative() || m.isAbstract()) { + // We can't edit native or abstract methods + Main.context.release(methods[j]); + continue; + } + + Main.bloatMethod(m, Main.context); + } + + if (Main.ANNO) { + String s = "Optimized with: EDU.purdue.cs.bloat.optimize.Main"; + + for (int i = 0; i < Main.ARGS.length; i++) { + if ((Main.ARGS[i].indexOf(' ') >= 0) + || (Main.ARGS[i].indexOf('\t') >= 0) + || (Main.ARGS[i].indexOf('\r') >= 0) + || (Main.ARGS[i].indexOf('\n') >= 0)) { + s += " '" + Main.ARGS[i] + "'"; + } else { + s += " " + Main.ARGS[i]; + } + } + + System.out.println(s); + // c.constants().addConstant(Constant.UTF8, s); + } + + Main.context.commit(classFile); + Main.context.release(classFile); + + if (Main.TRACE) { + System.out.println(Main.context.toString()); + } + } + + /** + * Runs BLOAT on a method. + */ + public static void bloatMethod(final MethodEditor m, + final BloatContext context) { + try { + if (Main.COMPACT_ARRAY_INIT) { + // Compact the initialization of arrays of the basic types by + // putting the values of the array into a string in the constant + // pool. The initialization code is replaced with a loop that + // loads the array from the string in the constant pool. + + if (Main.TRACE) { + System.out.println(" Compacting Arrays: " + + Main.dateFormat.format(new Date())); + } + + CompactArrayInitializer.transform(m); + + if (Main.DEBUG) { + System.out.println("---------- After compaction:"); + m.print(System.out); + System.out.println("---------- end print"); + } + } + + FlowGraph cfg; // The control flow graph for a method + + if (Main.TRACE) { + System.out.println(" Constructing CFG: " + + Main.dateFormat.format(new Date())); + } + + try { + // Construct the control flow graph for method m + cfg = new FlowGraph(m); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + context.release(m.methodInfo()); + return; + } + + // We separate out initialization since before this the FlowGraph + // more exactly represents the input program. + cfg.initialize(); + + if (Main.TRACE) { + System.out.println(" Transforming to SSA: " + + Main.dateFormat.format(new Date())); + } + + SSA.transform(cfg); + + if (FlowGraph.DEBUG) { + System.out.println("---------- After SSA:"); + cfg.print(System.out); + System.out.println("---------- end print"); + } + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + if (!Tree.USE_STACK) { + // Do copy propagation and value numbering first to get rid of + // all the extra copies inserted for dups. If they're left in, + // it really slows down value numbering. + if (Main.PROP) { + if (Main.DEBUG) { + System.out.println("-----Before Copy Propagation-----"); + } + + if (Main.TRACE) { + System.out.println(" Copy propagation: " + + Main.dateFormat.format(new Date())); + } + + final ExprPropagation copy = new ExprPropagation(cfg); + copy.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + if (Main.DEBUG) { + System.out.println("------After Copy Propagation-----"); + cfg.print(System.out); + } + } + } + + DeadCodeElimination dce = null; + + if (Main.DCE) { + + if (Main.TRACE) { + System.out.println(" Dead Code Elimination: " + + Main.dateFormat.format(new Date())); + } + + if (Main.DEBUG) { + System.out.println("---Before Dead Code Elimination--"); + } + + dce = new DeadCodeElimination(cfg); + dce.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG(false)); + } + + if (Main.DEBUG) { + System.out.println("---After Dead Code Elimination---"); + cfg.print(System.out); + } + } + + if (Main.INFER) { + + if (Main.DEBUG) { + System.out.println("---------Doing type inference--------"); + } + + if (Main.TRACE) { + System.out.println(" Type Inferencing: " + + Main.dateFormat.format(new Date())); + } + + TypeInference.transform(cfg, context.getHierarchy()); + } + + if (Main.NUMBER) { + + if (Main.TRACE) { + System.out.println(" Value Numbering: " + + Main.dateFormat.format(new Date())); + } + + if (Main.DEBUG) { + System.out.println("--------Doing value numbering--------"); + } + + (new ValueNumbering()).transform(cfg); + } + + if (Main.FOLD) { + if (Main.DEBUG) { + System.out.println("--------Before Value Folding---------"); + } + + if (Main.TRACE) { + System.out.println(" Value Folding: " + + Main.dateFormat.format(new Date())); + } + + (new ValueFolding()).transform(cfg); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("---------After Value Folding---------"); + cfg.print(System.out); + } + + } + + if (Main.PRE) { + if (Main.DEBUG) { + System.out.println("-------------Before SSAPRE-----------"); + } + + if (Main.TRACE) { + System.out.println(" SSAPRE: " + + Main.dateFormat.format(new Date())); + } + + final SSAPRE pre = new SSAPRE(cfg, context); + pre.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("-------------After SSAPRE------------"); + cfg.print(System.out); + } + } + + if (Main.FOLD) { + if (Main.DEBUG) { + System.out.println("--------Before Value Folding---------"); + } + + if (Main.TRACE) { + System.out.println(" Value Folding: " + + Main.dateFormat.format(new Date())); + } + + (new ValueFolding()).transform(cfg); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("---------After Value Folding---------"); + cfg.print(System.out); + } + + } + + if (Main.PROP) { + if (Main.DEBUG) { + System.out.println("-------Before Copy Propagation-------"); + } + + if (Main.TRACE) { + System.out.println(" Copy Propagation " + + Main.dateFormat.format(new Date())); + } + + final ExprPropagation copy = new ExprPropagation(cfg); + copy.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("--------After Copy Propagation-------"); + cfg.print(System.out); + } + } + + // make sure we've done at least one thing since the last DCE + if (Main.DCE + && (Main.INFER || Main.NUMBER || Main.FOLD || Main.PRE || Main.PROP)) { + if (Main.DEBUG) { + System.out.println("-----Before Dead Code Elimination----"); + } + + if (Main.TRACE) { + System.out.println(" Dead Code Elimination: " + + Main.dateFormat.format(new Date())); + } + + dce = new DeadCodeElimination(cfg); + dce.transform(); + + if (Main.DEBUG) { + cfg.visit(new VerifyCFG()); + } + + if (Main.DEBUG) { + System.out.println("-----After Dead Code Elimination-----"); + cfg.print(System.out); + } + } + + if (Main.PERSIST) { + (new PersistentCheckElimination()).transform(cfg); + } + + if (Main.DIVA) { + if (Main.DEBUG) { + System.out.println("-----Before DIVA------"); + } + + if (Main.TRACE) { + System.out.println(" DIVA: " + + Main.dateFormat.format(new Date())); + } + + (new InductionVarAnalyzer()).transform(cfg); + + if (Main.DEBUG) { + System.out.println("-----After DIVA-----"); + cfg.print(System.out); + } + + } + + /* + * if (STACK_ALLOC) { if (DEBUG) { + * System.out.println("------------Before StackPRE----------"); } + * + * StackPRE pre = new StackPRE(cfg); pre.transform(); + * + * if (DEBUG) { cfg.visit(new VerifyCFG()); } + * + * if (DEBUG) { System.out.println("------------After + * StackPRE-----------"); cfg.print(System.out); } } + */ + + // Do the new stack optimization + if (Main.OPT_STACK_2) { + + if (Main.TRACE) { + System.out.println(" New stack optimization: " + + Main.dateFormat.format(new Date())); + } + + // generate code without doing liveness or register allocation + final CodeGenerator codegen = new CodeGenerator(m); + codegen.replacePhis(cfg); + m.clearCode2(); + cfg.visit(codegen); + // do stack optimization on the bytecode + + final StackOpt so = new StackOpt(); + so.transform(m); + + // convert it back to a cfg + cfg = new FlowGraph(m); + cfg.initialize(); + + // convert it back to SSA + SSA.transform(cfg); + + // do more dead code elimination (eliminate stores) + dce = new DeadCodeElimination(cfg); + dce.transform(); + } + + if (Main.TRACE) { + System.out.println(" Register allocation: " + + Main.dateFormat.format(new Date())); + } + + if (Main.VERIFY) { + try { + cfg.visit(new VerifyCFG()); + } catch (final IllegalArgumentException ee) { + System.out.println(" NOTE: CFG did not verify while " + + "bloating " + m.name() + + " after all optimizations. Exception: " + ee); + } + } + + // We're all done performing optimizations. Let's generate some code + // and go home. + + // Perform liveness analysis of variables in the method. + // Assign local variables ("registers") to expression values. + final Liveness liveness = new Liveness(cfg); + final RegisterAllocator alloc = new RegisterAllocator(cfg, liveness); + + // Gather information which can be used to optimize use of the stack + if (CodeGenerator.OPT_STACK) { + if (Main.TRACE) { + System.out.println(" Old stack optimization: " + + Main.dateFormat.format(new Date())); + } + StackOptimizer.optimizeCFG(cfg); + } + + if (Main.TRACE) { + System.out.println(" Code Generation: " + + Main.dateFormat.format(new Date())); + } + + // Start the code generation process. + final CodeGenerator codegen = new CodeGenerator(m); + codegen.replacePhis(cfg); + + if (Main.DEBUG) { + System.out.println("After fixing Phis------------------------"); + cfg.print(System.out); + System.out.println("End print--------------------------------"); + } + + codegen.simplifyControlFlow(cfg); + codegen.allocReturnAddresses(cfg, alloc); + + if (Main.DEBUG) { + System.out.println("After removing empty blocks--------------"); + cfg.print(System.out); + System.out.println("End print--------------------------------"); + } + + // Clear the old contents of the bytecode store and generate new + // code. + // Code is generated using a visitor pattern on the CFG. + m.clearCode(); + cfg.visit(codegen); + + Peephole.transform(m); + + // Commit any changes that have been made to the method + context.commit(m.methodInfo()); + + } catch (final Exception ex99) { + final String msg = "** Exception while optimizing " + m.name() + + m.type() + " of class " + m.declaringClass().name(); + System.err.println(msg); + System.err.println(ex99.getMessage()); + ex99.printStackTrace(System.err); + System.exit(1); + } + } + + public static void dumpcode(final MethodEditor m) { + + final PrintWriter out = new PrintWriter(System.out, true); + final StackHeightCounter shc = new StackHeightCounter(m); + + out.println("Code for method " + m.name() + m.type()); + final List instructions = m.code(); + final ListIterator iter = instructions.listIterator(); + while (iter.hasNext()) { + final Object obj = iter.next(); + if (obj instanceof Label) { + shc.handle((Label) obj); + } else if (obj instanceof Instruction) { + shc.handle((Instruction) obj); + } + + System.out + .println(" " + obj + " (sh: " + shc.height() + ")"); + } + } + +} diff --git a/src/EDU/purdue/cs/bloat/optimize/Makefile b/src/EDU/purdue/cs/bloat/optimize/Makefile new file mode 100644 index 0000000..f9616df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/optimize/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + Main.class + +include ../class.mk 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/.cvsignore b/src/EDU/purdue/cs/bloat/reflect/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/reflect/Catch.java b/src/EDU/purdue/cs/bloat/reflect/Catch.java new file mode 100644 index 0000000..f6cb891 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/Catch.java @@ -0,0 +1,161 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * Catch stores information about a protected block and an exception handler in + * a method. The startPC, endPC, and handlerPC are indices into the bytecode of + * the method where the protected block begins and ends and the catch block + * begins, respectively. They are indices into the code array. + * + * @see EDU.purdue.cs.bloat.file.Code#code + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class Catch { + private int startPC; + + private int endPC; + + private int handlerPC; + + private int catchType; + + /** + * Constructor. + * + * @param startPC + * The start PC of the protected block. + * @param endPC + * The PC of the instruction after the end of the protected + * block. + * @param handlerPC + * The start PC of the exception handler. + * @param catchType + * The type of exception to catch. + */ + public Catch(final int startPC, final int endPC, final int handlerPC, + final int catchType) { + this.startPC = startPC; + this.endPC = endPC; + this.handlerPC = handlerPC; + this.catchType = catchType; + } + + /** + * Get the start PC of the protected block. + * + * @return The start PC of the protected block. + * @see Catch#setStartPC + */ + public int startPC() { + return startPC; + } + + /** + * Set the start PC of the protected block. + * + * @param pc + * The start PC of the protected block. + * @see Catch#startPC + */ + public void setStartPC(final int pc) { + startPC = pc; + } + + /** + * Get the end PC of the protected block. + * + * @return The PC of the instruction after the end of the protected block. + * @see Catch#setEndPC + */ + public int endPC() { + return endPC; + } + + /** + * Set the end PC of the protected block. + * + * @param pc + * The PC of the instruction after the end of the protected + * block. + * @see Catch#endPC + */ + public void setEndPC(final int pc) { + endPC = pc; + } + + /** + * Get the start PC of the exception handler. + * + * @return The start PC of the exception handler. + * @see Catch#setHandlerPC + */ + public int handlerPC() { + return handlerPC; + } + + /** + * Set the start PC of the exception handler. + * + * @param pc + * The start PC of the exception handler. + * @see Catch#handlerPC + */ + public void setHandlerPC(final int pc) { + handlerPC = pc; + } + + /** + * Get the index into the constant pool of the type of exception to catch. + * + * @return Index of the type of exception to catch. + * @see Catch#setCatchTypeIndex + */ + public int catchTypeIndex() { + return catchType; + } + + /** + * Set the index into the constant pool of the type of exception to catch. + * + * @param index + * Index of the type of exception to catch. + * @see Catch#catchTypeIndex + */ + public void setCatchTypeIndex(final int index) { + this.catchType = index; + } + + public Object clone() { + return (new Catch(this.startPC, this.endPC, this.handlerPC, + this.catchType)); + } + + /** + * Returns a string representation of the catch information. + */ + public String toString() { + return "(try-catch " + startPC + " " + endPC + " " + handlerPC + " " + + catchType + ")"; + } +} 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..aae1d41 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/ClassFormatException.java @@ -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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +public class ClassFormatException extends RuntimeException { + public ClassFormatException(final String msg) { + super(msg); + } +} diff --git a/src/EDU/purdue/cs/bloat/reflect/ClassInfo.java b/src/EDU/purdue/cs/bloat/reflect/ClassInfo.java new file mode 100644 index 0000000..0782af1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/ClassInfo.java @@ -0,0 +1,240 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +import java.util.*; + +/** + * ClassInfo allows a class to be accessed and modified at a very low level. + * ClassInfo is implemented by file.ClassFile + * + * @see EDU.purdue.cs.bloat.file.ClassFile + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface ClassInfo { + /** + * Get the class info loader for the class. + * + * @return The class info loader. + */ + public ClassInfoLoader loader(); + + /** + * Get the name of the class. + * + * @return The name of the class. + */ + public String name(); + + /** + * Get the index into the constant pool of class. + * + * @return The index of the class. + */ + public int classIndex(); + + /** + * Get the index into the constant pool of class's superclass. + * + * @return The index of the superclass. + */ + public int superclassIndex(); + + /** + * Get the indices into the constant pool of class's interfaces. + * + * @return The indices of the interfaces. + */ + public int[] interfaceIndices(); + + /** + * Set the index into the constant pool of class. + * + * @param index + * The index of the class. + */ + public void setClassIndex(int index); + + /** + * Set the index into the constant pool of class's superclass. + * + * @param index + * The index of the superclass. + */ + public void setSuperclassIndex(int index); + + /** + * Set the indices into the constant pool of class's interfaces. + * + * @param indices + * The indices of the interfaces. + */ + public void setInterfaceIndices(int[] indices); + + /** + * Set the modifiers of the class. The values correspond to the constants in + * the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the class. + * @see Modifiers + */ + public void setModifiers(int modifiers); + + /** + * Get the modifiers of the class. The values correspond to the constants in + * the Modifiers class. + * + * @return A bit vector of modifier flags for the class. + * @see Modifiers + */ + public int modifiers(); + + /** + * Get an array of FieldInfo structures for each field in the class. + * + * @return An array of FieldInfo structures. + * @see FieldInfo + */ + public FieldInfo[] fields(); + + /** + * Returns an array of MethodInfo structures for each method in the class. + */ + public MethodInfo[] methods(); + + /** + * Sets the methods in this class. + */ + public void setMethods(MethodInfo[] methods); + + /** + * Returns an array of the constants in the constant pool. + */ + public Constant[] constants(); + + /** + * Set all the constants in the constant pool. + * + * @param constants + * The array of Constants. + * @see Constant + */ + public void setConstants(Constant[] constants); + + /** + * Commit any changes to the file or to the virtual machine. + */ + public void commit(); + + /** + * Commits only certain methods and fields. + * + * @param methods + * Methods (MethodInfos) to commit. If null, + * all methods are committed. + * @param fields + * Fields (FieldInfos) to commit. If null, + * all fields are committed. + */ + public void commitOnly(Set methods, Set fields); + + /** + * Factory method that creates a new field in the class being modeled + */ + public FieldInfo addNewField(int modifiers, int typeIndex, int nameIndex); + + /** + * Factory method that creates a new field with a constant value in the + * class being modeled + * + * @param cvNameIndex + * The index in the class's constant pool for the UTF8 constant + * "ConstantValue" + * @param constantValueIndex + * The index in the class's constant pool for the constant value + */ + public FieldInfo addNewField(int modifiers, int typeIndex, int nameIndex, + int cvNameIndex, int constantValueIndex); + + /** + * Deletes a field from this class + * + * @param nameIndex + * Index in the constant pool of the name of the field to be + * deleted + * + * @throws IllegalArgumentException The class modeled by this + * ClassInfo does not contain a field whose name is at + * the given index + */ + public void deleteField(int nameIndex); + + /** + * Deletes a method from this class + * + * @param nameIndex + * Index in the constant pool of the name of the method to be + * deleted + * @param typeIndex + * Index in the constant pool of the type of the method to be + * deleted + * + * @throws IllegalArgumentException The class modeled by this + * ClassInfo does not contain a method whose name and + * type are not at the given indices + */ + public void deleteMethod(int nameIndex, int typeIndex); + + /** + * Adds a new method to this class. + * + * @param modifiers + * The {@link EDU.purdue.cs.bloat.reflect.Modifiers modifiers} + * for the new method + * @param typeIndex + * The index of the type (conglomeration of the parameter types + * and the return type) for this method in the class's constant + * pool + * @param nameIndex + * The index of the name of the method in the class's constant + * pool + * @param exceptionIndex + * The index of the UTF8 string "Exceptions" in the class's + * constant pool + * @param exceptionTypeIndices + * The indices in the class's constant pool of the type of the + * exceptions thrown by this method + * @param codeIndex + * The index of the UTF8 string "Code" in the class's constant + * pool + */ + public MethodInfo addNewMethod(int modifiers, int typeIndex, int nameIndex, + int exceptionIndex, int[] exceptionTypeIndices, int codeIndex); + + public void print(java.io.PrintStream out); + + public void print(java.io.PrintWriter out); + + public String toString(); +} diff --git a/src/EDU/purdue/cs/bloat/reflect/ClassInfoLoader.java b/src/EDU/purdue/cs/bloat/reflect/ClassInfoLoader.java new file mode 100644 index 0000000..d038cea --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/ClassInfoLoader.java @@ -0,0 +1,72 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +import java.io.*; + +/** + * ClassInfoLoader provides an interface for loading classes. Implementing + * classes can load classes from a file, from the JVM, or elsewhere. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface ClassInfoLoader { + /** + * Load a class. + * + * @param name + * The name of the class to load, including the package name. + * @return A ClassInfo for the class. + * @exception ClassNotFoundException + * The class cannot be found in the class path. + * @see ClassInfo + */ + public ClassInfo loadClass(String name) throws ClassNotFoundException; + + /** + * Creates a new class or interface. + * + * @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). + */ + public ClassInfo newClass(int modifiers, int classIndex, + int superClassIndex, int[] interfaceIndexes, + java.util.List constants); + + /** + * Returns an OutputStream to which a class should be + * written. + */ + OutputStream outputStreamFor(ClassInfo info) throws IOException; +} diff --git a/src/EDU/purdue/cs/bloat/reflect/Constant.java b/src/EDU/purdue/cs/bloat/reflect/Constant.java new file mode 100644 index 0000000..b11f539 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/Constant.java @@ -0,0 +1,244 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * A Constant is used to represent an item in the constant pool of a class. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public final class Constant { + private int tag; + + private Object value; + + /** + * Constant tag for class types. This is used to reference other classes, + * such as the superclass, and is used by the checkcast and instanceof + * instructions. The Fieldref, Methodref and InterfaceMethodref constant + * types refer to this constant type. + */ + public static final byte CLASS = 7; + + /** + * Constant tag for field references. This is used to reference a field in + * (possibly) another class. The getfield, putfield, getstatic, and + * putstatic instructions use this constant type. + */ + public static final byte FIELD_REF = 9; + + /** + * Constant tag for method references. This is used to reference a method in + * (possibly) another class. The invokevirtual, invokespecial, and + * invokestatic instructions use this constant type. + */ + public static final byte METHOD_REF = 10; + + /** + * Constant tag for java.lang.String constants. The actual string value is + * stored indirectly in a Utf8 constant. + */ + public static final byte STRING = 8; + + /** + * Constant tag for int, short, byte, char, and boolean constants. + */ + public static final byte INTEGER = 3; + + /** + * Constant tag for float constants. + */ + public static final byte FLOAT = 4; + + /** + * Constant tag for long constants. + */ + public static final byte LONG = 5; + + /** + * Constant tag for double constants. + */ + public static final byte DOUBLE = 6; + + /** + * Constant tag for method references. This is used to reference a method in + * an interface. The invokeinterface instruction uses this constant type. + */ + public static final byte INTERFACE_METHOD_REF = 11; + + /** + * Constant tag for holding the name and type of a field or method. The + * Fieldref, Methodref and InterfaceMethodref constant types refer to this + * constant type. + */ + public static final byte NAME_AND_TYPE = 12; + + /** + * Constant tag for holding the a UTF8 format string. The string is used to + * hold the name and type descriptor for NameandType constants, the class + * name for Class constants, the string value for String constants. + */ + public static final byte UTF8 = 1; + + /** + * @param tag + * The constant's tag. + * @param value + * The constant's value. + */ + public Constant(final int tag, final Object value) { + this.tag = tag; + this.value = value; + } + + /** + * Get the tag of the constant. + * + * @return The tag. + */ + public final int tag() { + return tag; + } + + /** + * Get the value of the constant. + * + * @return The value. + */ + public final Object value() { + return value; + } + + /** + * Hash the constant. + * + * @return The hash code. + */ + public int hashCode() { + switch (tag) { + case CLASS: + case STRING: + case INTEGER: + case FLOAT: + case LONG: + case DOUBLE: + case UTF8: + return tag ^ value.hashCode(); + case FIELD_REF: + case METHOD_REF: + case INTERFACE_METHOD_REF: + case NAME_AND_TYPE: + return tag ^ ((int[]) value)[0] ^ ((int[]) value)[1]; + } + + return tag; + } + + /** + * Check if an object is equal to this constant. + * + * @param other + * The object to compare against. + * @return true if equal, false if not. + */ + public boolean equals(final Object other) { + if (!(other instanceof Constant)) { + return false; + } + + final Constant c = (Constant) other; + + if (tag != c.tag) { + return false; + } + + switch (tag) { + case CLASS: + case STRING: + case INTEGER: + case FLOAT: + case LONG: + case DOUBLE: + case UTF8: + return value.equals(c.value); + case FIELD_REF: + case METHOD_REF: + case INTERFACE_METHOD_REF: + case NAME_AND_TYPE: + return (((int[]) value)[0] == ((int[]) c.value)[0]) + && (((int[]) value)[1] == ((int[]) c.value)[1]); + } + + return false; + } + + /** + * Convert the constant to a string. + * + * @return A string representation of the constant. + */ + public String toString() { + switch (tag) { + case CLASS: + return "Class " + value.toString(); + case STRING: + return "String " + value.toString(); + case INTEGER: + return "Integer " + value.toString(); + case FLOAT: + return "Float " + value.toString(); + case LONG: + return "Long " + value.toString(); + case DOUBLE: + return "Double " + value.toString(); + case UTF8: + final StringBuffer sb = new StringBuffer(); + final String s = (String) value; + for (int i = 0; i < s.length(); i++) { + final 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; + } + } + return "Utf8 '" + sb.toString() + "'"; + case FIELD_REF: + return "Fieldref " + ((int[]) value)[0] + " " + ((int[]) value)[1]; + case METHOD_REF: + return "Methodref " + ((int[]) value)[0] + " " + ((int[]) value)[1]; + case INTERFACE_METHOD_REF: + return "InterfaceMethodref " + ((int[]) value)[0] + " " + + ((int[]) value)[1]; + case NAME_AND_TYPE: + return "NameandType " + ((int[]) value)[0] + " " + + ((int[]) value)[1]; + } + + return "unknown constant"; + } +} diff --git a/src/EDU/purdue/cs/bloat/reflect/FieldInfo.java b/src/EDU/purdue/cs/bloat/reflect/FieldInfo.java new file mode 100644 index 0000000..c152077 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/FieldInfo.java @@ -0,0 +1,104 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * FieldInfo grants access to a field's name and type (represented as indices + * into the constant pool), as well as its modifiers. FieldInfo is implemented + * in file.Field. + * + * @see EDU.purdue.cs.bloat.file.Field + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface FieldInfo { + /** + * Get the class which declared the field. + * + * @return The ClassInfo of the class which declared the field. + */ + public ClassInfo declaringClass(); + + /** + * Get the index into the constant pool of the name of the field. + * + * @return The index into the constant pool of the name of the field. + */ + public int nameIndex(); + + /** + * Get the index into the constant pool of the type of the field. + * + * @return The index into the constant pool of the type of the field. + */ + public int typeIndex(); + + /** + * Set the index into the constant pool of the name of the field. + * + * @param index + * The index into the constant pool of the name of the field. + */ + public void setNameIndex(int index); + + /** + * Set the index into the constant pool of the type of the field. + * + * @param index + * The index into the constant pool of the type of the field. + */ + public void setTypeIndex(int index); + + /** + * Set the modifiers of the field. The values correspond to the constants in + * the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the field. + * @see Modifiers + */ + public void setModifiers(int modifiers); + + /** + * Get the modifiers of the field. The values correspond to the constants in + * the Modifiers class. + * + * @return A bit vector of modifier flags for the field. + * @see Modifiers + */ + public int modifiers(); + + /** + * Get the index into the constant pool of the field's constant value, if + * any. Returns 0 if the field does not have a constant value. + * + * @see ClassInfo#constants + */ + public int constantValue(); + + /** + * Set the index into the constant pool of the field's constant value. + * + * @see ClassInfo#constants + */ + public void setConstantValue(int index); +} diff --git a/src/EDU/purdue/cs/bloat/reflect/LineNumberDebugInfo.java b/src/EDU/purdue/cs/bloat/reflect/LineNumberDebugInfo.java new file mode 100644 index 0000000..0bc00f1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/LineNumberDebugInfo.java @@ -0,0 +1,81 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * LineNumberDebugInfo is used to map a range of instructions to a line number + * in the original Java source file. An instance of + * file.LineNumberTable contains an array of these guys. + * + * @see EDU.purdue.cs.bloat.file.LineNumberTable + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class LineNumberDebugInfo { + private int startPC; + + private int lineNumber; + + /** + * Constructor. + * + * @param startPC + * The start PC of the instructions for this line number. + * @param lineNumber + * The line number for this group of instructions. + */ + public LineNumberDebugInfo(final int startPC, final int lineNumber) { + this.startPC = startPC; + this.lineNumber = lineNumber; + } + + /** + * Get the start PC of the instructions for this line number. + * + * @return The start PC. + */ + public int startPC() { + return startPC; + } + + /** + * Get the line number for this group of instructions. + * + * @return The line number. + */ + public int lineNumber() { + return lineNumber; + } + + /** + * Convert the attribute to a string. + * + * @return A string representation of the attribute. + */ + public String toString() { + return "(line #" + lineNumber + " pc=" + startPC + ")"; + } + + public Object clone() { + return (new LineNumberDebugInfo(this.startPC, this.lineNumber)); + } +} diff --git a/src/EDU/purdue/cs/bloat/reflect/LocalDebugInfo.java b/src/EDU/purdue/cs/bloat/reflect/LocalDebugInfo.java new file mode 100644 index 0000000..05f410c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/LocalDebugInfo.java @@ -0,0 +1,134 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * LocalDebugInfo is used to map a local variable index in a range of + * instructions to a local in the original Java source file. In addition, + * LocalDebugInfo keeps track of a local variable's name and type (as indices + * into the constant pool) and which number local variable is being represented. + * Instances of file.LocalVariableTable consist of an array of + * LocalDebugInfo. + * + * @see EDU.purdue.cs.bloat.file.LocalVariableTable + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public class LocalDebugInfo { + private int startPC; + + private int length; + + private int nameIndex; + + private int typeIndex; + + private int index; + + /** + * Constructor. + * + * @param startPC + * The start PC of the live range of the variable. + * @param length + * The length of the live range of the variable. + * @param nameIndex + * The index into the constant pool of the name of the variable. + * @param typeIndex + * The index into the constant pool of the type descriptor of the + * variable. + * @param index + * The index of this variable into the local variable array for + * the method. + */ + public LocalDebugInfo(final int startPC, final int length, + final int nameIndex, final int typeIndex, final int index) { + this.startPC = startPC; + this.length = length; + this.nameIndex = nameIndex; + this.typeIndex = typeIndex; + this.index = index; + } + + /** + * Get the start PC of the live range of the variable. + * + * @return The start PC of the live range of the variable. + */ + public int startPC() { + return startPC; + } + + /** + * Get the length of the live range of the variable. + * + * @return The length of the live range of the variable. + */ + public int length() { + return length; + } + + /** + * Get the index into the constant pool of the name of the variable. + * + * @return The index into the constant pool of the name of the variable. + */ + public int nameIndex() { + return nameIndex; + } + + /** + * Get the index into the constant pool of the type descriptor of the + * variable. + * + * @return The index into the constant pool of the type descriptor of the + * variable. + */ + public int typeIndex() { + return typeIndex; + } + + /** + * Get the index of this variable into the local variable array for the + * method. + * + * @return The index of this variable into the local variable array for the + * method. + */ + public int index() { + return index; + } + + public Object clone() { + return (new LocalDebugInfo(this.startPC, this.length, this.nameIndex, + this.typeIndex, this.index)); + } + + /** + * Returns a string representation of the attribute. + */ + public String toString() { + return "(local #" + index + " pc=" + startPC + ".." + + (startPC + length) + " name=" + nameIndex + " desc=" + + typeIndex + ")"; + } +} diff --git a/src/EDU/purdue/cs/bloat/reflect/Makefile b/src/EDU/purdue/cs/bloat/reflect/Makefile new file mode 100644 index 0000000..e25add8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/Makefile @@ -0,0 +1,32 @@ +# 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. +# +# 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 + + +CLASS = \ + Catch.class\ + ClassInfo.class\ + ClassInfoLoader.class\ + ClassFormatException.class\ + Constant.class\ + FieldInfo.class\ + LineNumberDebugInfo.class\ + LocalDebugInfo.class\ + MethodInfo.class\ + Modifiers.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/reflect/MethodInfo.java b/src/EDU/purdue/cs/bloat/reflect/MethodInfo.java new file mode 100644 index 0000000..33d99df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/MethodInfo.java @@ -0,0 +1,187 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * MethodInfo provides methods for accessing and modifying a method. It is + * implemented by file.Method. + * + * @see EDU.purdue.cs.bloat.file.Method + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface MethodInfo { + /** + * Returns the class which declared the method. + */ + public ClassInfo declaringClass(); + + /** + * Returns the index into the constant pool of the name of the method. + */ + public int nameIndex(); + + /** + * Returns the index into the constant pool of the type of the method. + */ + public int typeIndex(); + + /** + * Sets the index into the constant pool of the name of the method. + */ + public void setNameIndex(int index); + + /** + * Set the index into the constant pool of the type of the method. + * + * @param index + * The index into the constant pool of the type of the method. + */ + public void setTypeIndex(int index); + + /** + * Set the modifiers of the method. The values correspond to the constants + * in the Modifiers class. + * + * @param modifiers + * A bit vector of modifier flags for the method. + * @see Modifiers + */ + public void setModifiers(int modifiers); + + /** + * Get the modifiers of the method. The values correspond to the constants + * in the Modifiers class. + * + * @return A bit vector of modifier flags for the method. + * @see Modifiers + */ + public int modifiers(); + + /** + * Get the indices into the constant pool of the types of the exceptions + * thrown by the method. + * + * @return The indices into the constant pool of the types of the exceptions + * thrown by the method. + */ + public int[] exceptionTypes(); + + /** + * Get the maximum height of the operand stack. + * + * @return The maximum height of the operand stack. + */ + public int maxStack(); + + /** + * Set the maximum height of the operand stack. + * + * @param maxStack + * The maximum height of the operand stack. + */ + public void setMaxStack(int maxStack); + + /** + * Get the maximum number of locals used in the method. + * + * @return The maximum number of locals used in the method. + */ + public int maxLocals(); + + /** + * Set the maximum number of locals used in the method. + * + * @param maxLocals + * The maximum number of locals used in the method. + */ + public void setMaxLocals(int maxLocals); + + /** + * Get the byte code array of the method. + * + * @return The byte code array of the method. + */ + public byte[] code(); + + /** + * Set the byte code array of the method. + * + * @param code + * The byte code array of the method. + */ + public void setCode(byte[] code); + + /** + * Get the line number debug info of the instructions in the method. + * + * @return The line numbers of the instructions in the method. The array + * will be of size 0 if the method has no line number debug info. + */ + public LineNumberDebugInfo[] lineNumbers(); + + /** + * Set the line number debug info of the instructions in the method. + * + * @param lineNumbers + * The line numbers of the instructions in the method. The array + * will be of size 0 if the method has no line number debug info. + */ + public void setLineNumbers(LineNumberDebugInfo[] lineNumbers); + + /** + * Get the local variable debug information for the method. + * + * @return The local variables in the method. The array will be of size 0 if + * the method has no local variable debug info. + */ + public LocalDebugInfo[] locals(); + + /** + * Set the local variables in the method. + * + * @param locals + * The local variables in the method. + */ + public void setLocals(LocalDebugInfo[] locals); + + /** + * Get the exception handlers in the method. + * + * @return The exception handlers in the method. + */ + public Catch[] exceptionHandlers(); + + /** + * Set the exception handlers in the method. + * + * @param exceptions + * The exception handlers in the method. + */ + public void setExceptionHandlers(Catch[] exceptions); + + /** + * Creates a clone of this MethodInfo except that its declaring + * class does not know about it. + */ + public Object clone(); +} diff --git a/src/EDU/purdue/cs/bloat/reflect/Modifiers.java b/src/EDU/purdue/cs/bloat/reflect/Modifiers.java new file mode 100644 index 0000000..bdbd25d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/reflect/Modifiers.java @@ -0,0 +1,90 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.reflect; + +/** + * Modifiers is an interface containing constants used as modifiers of classes, + * fields, and methods. + * + * @author Nate Nystrom (nystrom@cs.purdue.edu) + */ +public interface Modifiers { + /** + * The class, field, or method is declared public. + */ + public static final short PUBLIC = 0x0001; + + /** + * The class, field, or method is declared private. + */ + public static final short PRIVATE = 0x0002; + + /** + * The class, field, or method is declared protected. + */ + public static final short PROTECTED = 0x0004; + + /** + * The field or method is declared static. + */ + public static final short STATIC = 0x0008; + + /** + * The class, field, or method is declared final. + */ + public static final short FINAL = 0x0010; + + /** + * The class calls methods in the superclass. + */ + public static final short SUPER = 0x0020; + + /** + * The method is declared synchronized. + */ + public static final short SYNCHRONIZED = 0x0020; + + /** + * The field is declared volatile. + */ + public static final short VOLATILE = 0x0040; + + /** + * The field is declared transient. + */ + public static final short TRANSIENT = 0x0080; + + /** + * The method is declared native. + */ + public static final short NATIVE = 0x0100; + + /** + * The class is an interface. + */ + public static final short INTERFACE = 0x0200; + + /** + * The class or method is declared abstract. + */ + public static final short ABSTRACT = 0x0400; +} 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/shrink/.cvsignore b/src/EDU/purdue/cs/bloat/shrink/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/shrink/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/shrink/Main.java b/src/EDU/purdue/cs/bloat/shrink/Main.java new file mode 100644 index 0000000..a735f44 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/shrink/Main.java @@ -0,0 +1,315 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.shrink; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.trans.*; + +/** + * This program just performs array initialization compaction on a class. It + * really doesn't seem necessary anymore. + */ +public class Main implements Opcode { + private static int VERBOSE = 0; + + private static boolean FORCE = false; + + private static boolean CLOSURE = false; + + private static final List SKIP = new ArrayList(); + + private static final List ONLY = new ArrayList(); + + public static void main(final String[] args) { + final ClassFileLoader loader = new ClassFileLoader(); + List classes = new ArrayList(); + boolean gotdir = false; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + Main.VERBOSE++; + } else if (args[i].equals("-help")) { + Main.usage(); + } else if (args[i].equals("-classpath/p")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.prependClassPath(classpath); + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.setClassPath(classpath); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.SKIP.add(pkg); + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.ONLY.add(pkg); + } else if (args[i].equals("-closure")) { + Main.CLOSURE = true; + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + } else if (args[i].equals("-f")) { + Main.FORCE = true; + } else if (args[i].startsWith("-")) { + Main.usage(); + } else if (i == args.length - 1) { + final File f = new File(args[i]); + + if (f.exists() && !f.isDirectory()) { + System.err.println("No such directory: " + f.getPath()); + System.exit(2); + } + + loader.setOutputDir(f); + gotdir = true; + } else { + classes.add(args[i]); + } + } + + if (!gotdir) { + Main.usage(); + } + + if (classes.size() == 0) { + Main.usage(); + } + + if (Main.VERBOSE > 3) { + ClassFileLoader.DEBUG = true; + CompactArrayInitializer.DEBUG = true; + ClassEditor.DEBUG = true; + } + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + loader.loadClass(name); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + final BloatContext context = new CachingBloatContext(loader, classes, + Main.CLOSURE); + + if (!Main.CLOSURE) { + final Iterator e = classes.iterator(); + + while (e.hasNext()) { + final String name = (String) e.next(); + try { + final ClassInfo info = loader.loadClass(name); + Main.editClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } else { + classes = null; + + final ClassHierarchy hier = context.getHierarchy(); + + final Iterator e = hier.classes().iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + try { + final ClassInfo info = loader.loadClass(t.className()); + Main.editClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } + } + } + + private static void usage() { + System.err + .println("Usage: java EDU.purdue.cs.bloat.shrink.Main " + + "\n [-options] classes output_dir" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -v -verbose turn on verbose mode " + + "(can be given multiple times)" + + "\n -classpath " + + "\n list directories in which to look for classes" + + "\n -f decorate files even if up-to-date" + + "\n -closure recursively decorate 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 (this option can be given more than once)" + + "\n -only " + + "\n skip all but the given class or package" + + "\n (this option can be given more than once)"); + System.exit(0); + } + + private static void editClass(final EditorContext editor, + final ClassInfo info) { + final ClassFile classFile = (ClassFile) info; + + if (!Main.FORCE) { + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (Main.VERBOSE > 1) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (Main.VERBOSE > 2) { + classFile.print(System.out); + } + + final ClassEditor c = editor.editClass(info); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (Main.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < Main.ONLY.size(); i++) { + final String pkg = (String) Main.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 < Main.SKIP.size(); i++) { + final String pkg = (String) Main.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (Main.VERBOSE > 0) { + System.out.println("Skipping " + c.type().className()); + } + + editor.release(info); + return; + } + + if (Main.VERBOSE > 0) { + System.out.println("Decorating class " + c.type().className()); + } + + if (Main.VERBOSE > 2) { + ((ClassFile) info).print(System.out); + } + + boolean changed = false; + + final MethodInfo[] methods = c.methods(); + + for (int j = 0; j < methods.length; j++) { + MethodEditor m; + + try { + m = editor.editMethod(methods[j]); + } catch (final ClassFormatException ex) { + System.err.println(ex.getMessage()); + continue; + } + + if (CompactArrayInitializer.transform(m)) { + changed = true; + + if (Main.VERBOSE > 2) { + System.out.println("commit " + m.name() + " " + m.type()); + } + + editor.commit(methods[j]); + } else { + if (Main.VERBOSE > 2) { + System.out.println("release " + m.name() + " " + m.type()); + } + + editor.release(methods[j]); + } + } + + if (changed) { + editor.commit(info); + } + } +} diff --git a/src/EDU/purdue/cs/bloat/shrink/Makefile b/src/EDU/purdue/cs/bloat/shrink/Makefile new file mode 100644 index 0000000..f9616df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/shrink/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + Main.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/shrink/package.html b/src/EDU/purdue/cs/bloat/shrink/package.html new file mode 100644 index 0000000..99ee32e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/shrink/package.html @@ -0,0 +1,8 @@ + + + +

Contains a program that performs array initialization compaction on +a Java classfile in attempts to make it smaller.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/ssa/.cvsignore b/src/EDU/purdue/cs/bloat/ssa/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file 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..270ba24 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/ComponentVisitor.java @@ -0,0 +1,33 @@ +/** + * 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. + * + * 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 + */ + +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..d1d25e8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/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. +# +# 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 + + +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..ff569a9 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/PhiReturnStmt.java @@ -0,0 +1,140 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.ssa; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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 { + Subroutine sub; + + 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(final VarExpr target, final Subroutine sub) { + super(target); + this.sub = sub; + this.operand = (VarExpr) target.clone(); + operand.setParent(this); + operand.setDef(null); + } + + public void visitForceChildren(final TreeVisitor visitor) { + operand.visit(visitor); + } + + public void visit(final 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() { + final 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..99e4946 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/SSA.java @@ -0,0 +1,555 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.ssa; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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 { + public static boolean DEBUG = false; + + /** + * 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(FlowGraph) + * @see SSAConstructionInfo + */ + public static void transform(final FlowGraph cfg) { + final Iterator e = SSA.collectVars(cfg); + + while (e.hasNext()) { + final SSAConstructionInfo info = (SSAConstructionInfo) e.next(); + SSA.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 static void transform(final FlowGraph cfg, + final SSAConstructionInfo info) { + if (SSA.DEBUG) { + System.out.println("transforming " + info.prototype + " (" + + info.prototype.type() + ")"); + } + + SSA.placePhiFunctions(cfg, info); + SSA.rename(cfg, info); + SSA.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 static Iterator collectVars(final FlowGraph cfg) { + // SSAConstructionInfo objects for cfg + final Map infos = new HashMap(); + + cfg.visit(new TreeVisitor() { + // Visit all statements in the CFG. Remove any pre-existing + // PhiStmts. + public void visitTree(final Tree tree) { + final Iterator iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + final 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(final VarExpr expr) { + expr.visitChildren(this); + + expr.setDef(null); + + final 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 static void placePhiFunctions(final FlowGraph cfg, + final SSAConstructionInfo info) { + if (SSA.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. + final BitSet killed = new BitSet(cfg.size()); + + // Is the variable used in more than one block? + boolean nonLocal = false; + + final 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()) { + final VarExpr real = (VarExpr) reals.next(); + + final 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. + final Iterator catchBlocks = cfg.catchBlocks().iterator(); + + while (catchBlocks.hasNext()) { + final Block block = (Block) catchBlocks.next(); + info.addCatchPhi(block); + info.addDefBlock(block); + } + + // Iterate over all of the subroutines (finally blocks) and insert + // PhiReturnStmts where appropriate. + final Iterator subs = cfg.subroutines().iterator(); + + while (subs.hasNext()) { + final Subroutine sub = (Subroutine) subs.next(); + info.addRetPhis(sub); + + final Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + final 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. + final Iterator df = cfg.iteratedDomFrontier(info.defBlocks()) + .iterator(); + + while (df.hasNext()) { + final 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 static void addCatchPhiOperands(final SSAConstructionInfo info, + final Block block, final LocalExpr def) { + final 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()) { + final Handler handler = (Handler) handlers.next(); + + if (handler.protectedBlocks().contains(block)) { + final PhiCatchStmt phi = (PhiCatchStmt) info.phiAtBlock(handler + .catchBlock()); + + if ((phi != null) && !phi.hasOperandDef(def)) { + final 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 static void rename(final FlowGraph cfg, + final SSAConstructionInfo info) { + SSA.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; + + final Iterator subs = cfg.subroutines().iterator(); + + while (subs.hasNext()) { + final Subroutine sub = (Subroutine) subs.next(); + final Iterator paths = sub.paths().iterator(); + + final 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()) { + final Block[] path = (Block[]) paths.next(); + + final 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(); + + final Iterator uses = ret.target().uses().iterator(); + + while (uses.hasNext()) { + final VarExpr use = (VarExpr) uses.next(); + use.setDef(def); + } + + // The PhiReturnStmt is no longer needed + info.removePhiAtBlock(path[1]); + changed = true; + } + } + } + } + + final Iterator subs = cfg.subroutines().iterator(); + + // Examine any remaining PhiReturnStmts. Replace all uses of the + // target of the PhiReturnStmt with its operand. + while (subs.hasNext()) { + final Subroutine sub = (Subroutine) subs.next(); + + final Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + final Block[] path = (Block[]) paths.next(); + + final PhiReturnStmt ret = (PhiReturnStmt) info + .phiAtBlock(path[1]); + + if (ret != null) { + final DefExpr def = ret.operand().def(); + + final Iterator uses = ret.target().uses().iterator(); + + while (uses.hasNext()) { + final 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 static void search(final FlowGraph cfg, + final SSAConstructionInfo info, VarExpr top, final Block block) { + if (SSA.DEBUG) { + System.out.println(" renaming " + info.prototype + " in " + block); + } + + // If appropriate, add top as an operand of a PhiCatchStmt + if (top instanceof LocalExpr) { + SSA.addCatchPhiOperands(info, block, (LocalExpr) top); + } + + // First handle any phi in the block. + final PhiStmt phi = info.phiAtBlock(block); + + if (phi != null) { + top = phi.target(); + + if (top instanceof LocalExpr) { + SSA.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 (SSA.DEBUG) { + System.out.println(" Killing TOS at " + block); + } + + // The operand stack is popped down to 0 at catch blocks. + top = null; + } + + final 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()) { + final VarExpr real = (VarExpr) e.next(); + + if (real.isDef()) { + real.setDef(null); + + top = real; // A definition means a new SSA variable + + if (top instanceof LocalExpr) { + SSA.addCatchPhiOperands(info, block, (LocalExpr) top); + } + + if (SSA.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); + } + } + + final 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()) { + final Block succ = (Block) succs.next(); + + final PhiStmt succPhi = info.phiAtBlock(succ); + + if (succPhi instanceof PhiJoinStmt) { + final PhiJoinStmt f = (PhiJoinStmt) succPhi; + final VarExpr operand = (VarExpr) f.operandAt(block); + operand.setDef(top); + + } else if (succPhi instanceof PhiReturnStmt) { + final PhiReturnStmt f = (PhiReturnStmt) succPhi; + final VarExpr operand = (VarExpr) f.operand(); + operand.setDef(top); + } + + // Adjust the operands of any PhiCatchStmts if the sucessor node + // is protected. + if (top instanceof LocalExpr) { + SSA.addCatchPhiOperands(info, succ, (LocalExpr) top); + } + } + + final Iterator children = cfg.domChildren(block).iterator(); + + // Visit the children in the dominator tree. Keep the same most + // recent SSA variable (top). + while (children.hasNext()) { + final Block child = (Block) children.next(); + SSA.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 static void insertCode(final FlowGraph cfg, + final SSAConstructionInfo info) { + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final PhiStmt phi = info.phiAtBlock(block); + + if (phi != null) { + Assert.isFalse(phi instanceof PhiReturnStmt); + block.tree().prependStmt(phi); + } + } + } +} 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..5b106e9 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/SSAConstructionInfo.java @@ -0,0 +1,271 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.ssa; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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(final FlowGraph cfg, final VarExpr expr) { + this.cfg = cfg; + + prototype = (VarExpr) expr.clone(); + prototype.setDef(null); + + reals = new LinkedList[cfg.size()]; + allReals = new LinkedList(); + + defBlocks = new HashSet(); + + 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(final 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(final Block block) { + return phis[cfg.preOrderIndex(block)]; + } + + /** + * Removes the phi statement for this variable at a given block. + */ + public void removePhiAtBlock(final Block block) { + final 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(final Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + final VarExpr target = (VarExpr) prototype.clone(); + + final 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(final Subroutine sub) { + final Iterator paths = sub.paths().iterator(); + + while (paths.hasNext()) { + final 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(final Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + if (prototype instanceof LocalExpr) { + final LocalExpr target = (LocalExpr) prototype.clone(); + + final 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(final Subroutine sub, final Block block) { + if (phis[cfg.preOrderIndex(block)] != null) { + return; + } + + final VarExpr target = (VarExpr) prototype.clone(); + + final 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(final VarExpr real) { + if (real.stmt() instanceof PhiStmt) { + return; + } + + final 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(final 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..429e461 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/ssa/SSAGraph.java @@ -0,0 +1,703 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.ssa; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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; + + HashMap equiv; // A mapping between a Node and all its equivalent Nodes + + /** + * Grumble. + */ + public SSAGraph(final FlowGraph cfg, final 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(final FlowGraph cfg) { + this.cfg = cfg; + this.equiv = new HashMap(); + + cfg.visit(new TreeVisitor() { + // The CheckExpr and the Expr is checks are equivalent. + public void visitCheckExpr(final CheckExpr expr) { + expr.visitChildren(this); + makeEquiv(expr, expr.expr()); + } + + // The target of the PhiStmt and the PhiStmt are equivalent + public void visitPhiStmt(final PhiStmt stmt) { + stmt.visitChildren(this); + makeEquiv(stmt.target(), stmt); + } + + // The use of a variable and its defining variable are equivalent + public void visitVarExpr(final VarExpr expr) { + if (!expr.isDef()) { + final 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(final StackManipStmt stmt) { + final StackExpr[] target = stmt.target(); + final 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(final StackExpr[] source, + final StackExpr[] target, final 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(final 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(final Node node) { + Set s = (Set) equiv.get(node); + + if (s == null) { + s = new HashSet(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(final Node node1, final Node node2) { + final Set s1 = equivalent(node1); + final Set s2 = equivalent(node2); + + if (s1 != s2) { + s1.addAll(s2); + + final Iterator iter = s2.iterator(); + + while (iter.hasNext()) { + final 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(final Node node) { + final ArrayList c = new ArrayList(); + + if (node instanceof StoreExpr) { + final 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(final 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) { + final PhiStmt phi = (PhiStmt) node; + c.addAll(phi.operands()); + + } else { + node.visitChildren(new TreeVisitor() { + public void visitNode(final 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(); + final 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()) { + final Block block = (Block) iter.previous(); + + block.visit(new TreeVisitor() { + public void visitTree(final Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(final 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(final FlowGraph cfg) { + final ListIterator e = postOrder.listIterator(postOrder.size()); + + while (e.hasPrevious()) { + final Block block = (Block) e.previous(); + block.visit(this); + } + } + + public void visitTree(final 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(final 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()) { + final 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. + final Node grandParent = parent; + parent = node; + + equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + final Node e = (Node) equiv.next(); + + final Iterator children = children(e).iterator(); + + while (children.hasNext()) { + final 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) { + final ArrayList scc = new ArrayList(); + + while (!stack.isEmpty()) { + final 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(final Object a, final Object b) { + final int ka = ((Node) a).key(); + final int kb = ((Node) b).key(); + return ka - kb; + } + }); + + if (SSAGraph.DEBUG) { + System.out.print("SCC ="); + + final Iterator e = scc.iterator(); + + while (e.hasNext()) { + final 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) { + final 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) { + final 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(); + final 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()) { + final Block block = (Block) iter.previous(); + + block.visit(new TreeVisitor() { + public void visitTree(final Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(final 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(final FlowGraph cfg) { + final ListIterator e = postOrder.listIterator(postOrder.size()); + + while (e.hasPrevious()) { + final Block block = (Block) e.previous(); + block.visit(this); + } + } + + public void visitTree(final 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(final 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()) { + final 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. + final Node grandParent = parent; + parent = node; + + equiv = equivalent(node).iterator(); + + while (equiv.hasNext()) { + final Node e = (Node) equiv.next(); + + final Iterator children = children(e).iterator(); + + while (children.hasNext()) { + final 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) { + final ArrayList scc = new ArrayList(); + + while (!stack.isEmpty()) { + final 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(final Object a, final Object b) { + final int ka = ((Node) a).key(); + final int kb = ((Node) b).key(); + return ka - kb; + } + }); + + if (SSAGraph.DEBUG) { + System.out.print("SCC ="); + + final Iterator e = scc.iterator(); + + while (e.hasNext()) { + final 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) { + final 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) { + final 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(final PrintWriter pw) { + final Collection equivs = this.equivalences(); // A Collection of Sets + final Iterator iter = equivs.iterator(); + + pw.println("Strongly Connected Components of the SSAGraph"); + + for (int i = 1; iter.hasNext(); i++) { + final Set scc = (Set) iter.next(); + final Iterator sccIter = scc.iterator(); + + pw.println(" Component " + i); + + while (sccIter.hasNext()) { + final 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/strip/.cvsignore b/src/EDU/purdue/cs/bloat/strip/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/strip/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/strip/Main.java b/src/EDU/purdue/cs/bloat/strip/Main.java new file mode 100644 index 0000000..444c083 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/strip/Main.java @@ -0,0 +1,289 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.strip; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.reflect.*; + +/** + * This class is a driver program that uses BLOAT to make a Java class file + * smaller by removing all non-essential information (such as debugging + * information) from it. + */ +public class Main implements Opcode { + private static int VERBOSE = 0; + + private static boolean FORCE = false; + + private static boolean CLOSURE = false; + + private static final List SKIP = new ArrayList(); + + private static final List ONLY = new ArrayList(); + + public static void main(final String[] args) { + final ClassFileLoader loader = new ClassFileLoader(); + List classes = new ArrayList(); + boolean gotdir = false; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-v") || args[i].equals("-verbose")) { + Main.VERBOSE++; + } else if (args[i].equals("-help")) { + Main.usage(); + } else if (args[i].equals("-classpath")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.setClassPath(classpath); + } else if (args[i].equals("-classpath/p")) { + if (++i >= args.length) { + Main.usage(); + } + + final String classpath = args[i]; + loader.prependClassPath(classpath); + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.SKIP.add(pkg); + } else if (args[i].equals("-only")) { + if (++i >= args.length) { + Main.usage(); + } + + final String pkg = args[i].replace('.', '/'); + Main.ONLY.add(pkg); + } else if (args[i].equals("-closure")) { + Main.CLOSURE = true; + } else if (args[i].equals("-relax-loading")) { + ClassHierarchy.RELAX = true; + } else if (args[i].equals("-f")) { + Main.FORCE = true; + } else if (args[i].startsWith("-")) { + Main.usage(); + } else if (i == args.length - 1) { + final File f = new File(args[i]); + + if (f.exists() && !f.isDirectory()) { + System.err.println("No such directory: " + f.getPath()); + System.exit(2); + } + + loader.setOutputDir(f); + gotdir = true; + } else { + classes.add(args[i]); + } + } + + if (!gotdir) { + Main.usage(); + } + + if (classes.size() == 0) { + Main.usage(); + } + + if (Main.VERBOSE > 3) { + ClassFileLoader.DEBUG = true; + ClassEditor.DEBUG = true; + } + + boolean errors = false; + + final Iterator iter = classes.iterator(); + + while (iter.hasNext()) { + final String name = (String) iter.next(); + + try { + loader.loadClass(name); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + ex.getMessage()); + errors = true; + } + } + + if (errors) { + System.exit(1); + } + + final BloatContext context = new CachingBloatContext(loader, classes, + Main.CLOSURE); + + if (!Main.CLOSURE) { + final Iterator e = classes.iterator(); + + while (e.hasNext()) { + final String name = (String) e.next(); + try { + final ClassInfo info = loader.loadClass(name); + Main.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } else { + classes = null; + + final ClassHierarchy hier = context.getHierarchy(); + + final Iterator e = hier.classes().iterator(); + + while (e.hasNext()) { + final Type t = (Type) e.next(); + + if (t.isObject()) { + try { + final ClassInfo info = loader.loadClass(t.className()); + Main.decorateClass(context, info); + } catch (final ClassNotFoundException ex) { + System.err.println("Couldn't find class: " + + ex.getMessage()); + System.exit(1); + } + } + } + } + } + + private static void usage() { + System.err + .println("Usage: java EDU.purdue.cs.bloat.decorate.Main " + + "\n [-options] classes output_dir" + + "\n" + + "\nwhere options include:" + + "\n -help print out this message" + + "\n -v -verbose turn on verbose mode " + + "(can be given multiple times)" + + "\n -classpath " + + "\n list directories in which to look for classes" + + "\n -f decorate files even if up-to-date" + + "\n -closure recursively decorate 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 (this option can be given more than once)" + + "\n -only " + + "\n skip all but the given class or package" + + "\n (this option can be given more than once)"); + System.exit(0); + } + + private static void decorateClass(final EditorContext editor, + final ClassInfo info) { + final ClassFile classFile = (ClassFile) info; + + if (!Main.FORCE) { + final File source = classFile.file(); + final File target = classFile.outputFile(); + + if ((source != null) && (target != null) && source.exists() + && target.exists() + && (source.lastModified() < target.lastModified())) { + + if (Main.VERBOSE > 1) { + System.out.println(classFile.name() + " is up to date"); + } + + return; + } + } + + if (Main.VERBOSE > 2) { + classFile.print(System.out); + } + + final ClassEditor c = editor.editClass(info); + + boolean skip = false; + + final String name = c.type().className(); + final String qual = c.type().qualifier() + "/*"; + + // Edit only classes explicitly mentioned. + if (Main.ONLY.size() > 0) { + skip = true; + + // Only edit classes we explicitly don't name. + for (int i = 0; i < Main.ONLY.size(); i++) { + final String pkg = (String) Main.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 < Main.SKIP.size(); i++) { + final String pkg = (String) Main.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + } + + if (skip) { + if (Main.VERBOSE > 0) { + System.out.println("Skipping " + c.type().className()); + } + + editor.release(info); + return; + } + + if (Main.VERBOSE > 0) { + System.out.println("Stripping class " + c.type().className()); + } + + if (Main.VERBOSE > 2) { + ((ClassFile) info).print(System.out); + } + + final MethodInfo[] methods = c.methods(); + + for (int j = 0; j < methods.length; j++) { + methods[j].setLineNumbers(null); + methods[j].setLocals(null); + editor.commit(methods[j]); + } + + editor.commit(info); + } +} diff --git a/src/EDU/purdue/cs/bloat/strip/Makefile b/src/EDU/purdue/cs/bloat/strip/Makefile new file mode 100644 index 0000000..f9616df --- /dev/null +++ b/src/EDU/purdue/cs/bloat/strip/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + Main.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/strip/package.html b/src/EDU/purdue/cs/bloat/strip/package.html new file mode 100644 index 0000000..8401b7a --- /dev/null +++ b/src/EDU/purdue/cs/bloat/strip/package.html @@ -0,0 +1,9 @@ + + + +

Contains a program that removes non-vital information (such as +debugging information) from a Java classfile in an attempt to make it +smaller.

+ + + \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/tbaa/.cvsignore b/src/EDU/purdue/cs/bloat/tbaa/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tbaa/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..3124145 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tbaa/Makefile @@ -0,0 +1,24 @@ +# 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. +# +# 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 + + +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..10ef5a9 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tbaa/TBAA.java @@ -0,0 +1,245 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tbaa; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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(final EditorContext context, final Expr a, + final 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; + } + + final ClassHierarchy hier = context.getHierarchy(); + + // Only type-compatible arrays can alias the same location. + if ((a instanceof ArrayRefExpr) && (b instanceof ArrayRefExpr)) { + final ArrayRefExpr aa = (ArrayRefExpr) a; + final ArrayRefExpr bb = (ArrayRefExpr) b; + + final Type aaIndexType = aa.index().type(); + final Type bbIndexType = bb.index().type(); + final Type aaArrayType = aa.array().type(); + final Type bbArrayType = bb.array().type(); + + Assert.isTrue(aaIndexType.isIntegral(), aa.index() + " in " + aa + + " (" + aaIndexType + ") is not an integer"); + Assert.isTrue(bbIndexType.isIntegral(), bb.index() + " in " + bb + + " (" + bbIndexType + ") is not an integer"); + Assert.isTrue(aaArrayType.isArray() + || aaArrayType.equals(Type.OBJECT) + || aaArrayType.equals(Type.SERIALIZABLE) + || aaArrayType.equals(Type.CLONEABLE) + || aaArrayType.isNull(), aa.array() + " in " + aa + " (" + + aaArrayType + ") is not an array"); + Assert.isTrue(bbArrayType.isArray() + || bbArrayType.equals(Type.OBJECT) + || bbArrayType.equals(Type.SERIALIZABLE) + || bbArrayType.equals(Type.CLONEABLE) + || bbArrayType.isNull(), 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)) { + + final ConstantExpr ai = (ConstantExpr) aa.index(); + final ConstantExpr bi = (ConstantExpr) bb.index(); + + if ((ai.value() != null) && (bi.value() != null)) { + if (!ai.value().equals(bi.value())) { + return false; + } + } + } + + return TBAA.intersects(hier, aaArrayType, bbArrayType); + } + + try { + if (af != null) { + final FieldEditor e = context.editField(af); + + if (e.isVolatile()) { + context.release(e.fieldInfo()); + return true; + } + + if (e.isFinal()) { + context.release(e.fieldInfo()); + return false; + } + + context.release(e.fieldInfo()); + } + + if (bf != null) { + final FieldEditor e = context.editField(bf); + + if (e.isVolatile()) { + context.release(e.fieldInfo()); + return true; + } + + if (e.isFinal()) { + context.release(e.fieldInfo()); + return false; + } + + context.release(e.fieldInfo()); + } + + } catch (final NoSuchFieldException e) { + // 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 TBAA.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(final ClassHierarchy hier, final Type a, + final Type b) { + return !hier.intersectType(a, b).isNull(); + } +} 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..033b47e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tbaa/TypeInference.java @@ -0,0 +1,749 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tbaa; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 { + public static boolean DEBUG = false; + + static final Type UNDEF = Type.getType("Lundef!;"); + + /** + * 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 hier + * A class heirarchy used for getting information about Types + * (classes). + * + * @see SSAGraph + * @see ComponentVisitor + * @see TreeVisitor + * + */ + public static void transform(final FlowGraph cfg, final ClassHierarchy hier) { + + // 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(final InitStmt stmt) { + // a := INIT + // [declared param type] <= [a] + + final MethodEditor method = stmt.block().graph().method(); + + final LocalExpr[] t = stmt.targets(); + + for (int i = 0; i < t.length; i++) { + final 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 = method.declaringClass().type(); + + } else { + // One of the method's arguments is being initialized. + // Figure out its type. + type = method.type().indexedParamTypes()[index]; + } + + var.setType(type); + + final Iterator uses = var.uses().iterator(); + + // Set the type of the uses of the local variable, also. + while (uses.hasNext()) { + final LocalExpr use = (LocalExpr) uses.next(); + use.setType(type); + } + } + } + + public void visitExpr(final Expr expr) { + expr.visitChildren(this); + + // We don't know the type of the expression yet. + expr.setType(TypeInference.UNDEF); + } + }); + + final 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(final List scc) { + visitor.changed = true; + + while (visitor.changed) { + visitor.changed = false; + + final Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + final 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(final 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 (TypeInference.DEBUG) { + // Print out all the type information and do some checking. + cfg.visit(new TreeVisitor() { + public void visitExpr(final Expr expr) { + expr.visitChildren(this); + + System.out.println("typeof(" + expr + ") = " + expr.type()); + + if (expr.type().equals(TypeInference.UNDEF)) { + System.out.println("WARNING: typeof(" + expr + + ") = UNDEF"); + } + + Assert + .isFalse(expr.type().equals( + ClassHierarchy.POS_SHORT)); + Assert.isFalse(expr.type().equals(ClassHierarchy.POS_BYTE)); + } + }); + } + } +} + +/** + * 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(final ClassHierarchy hier, + final SSAGraph ssaGraph) { + this.hier = hier; + this.ssaGraph = ssaGraph; + } + + public void visitExpr(final Expr expr) { + throw new RuntimeException(expr + " not supported"); + } + + // Make a new start constraint for the expression being shifted. + public void visitShiftExpr(final ShiftExpr expr) { + if (expr.expr().type().isIntegral() + || expr.expr().type().equals(ClassHierarchy.POS_SHORT) + || expr.expr().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INTEGER); + + } else { + prop(expr, expr.expr()); + } + } + + // If either of the ArithExpr's operands has type INTEGER, than 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(final ArithExpr expr) { + if (expr.left().type().isIntegral() + || expr.left().type().equals(ClassHierarchy.POS_SHORT) + || expr.left().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INTEGER); + + } else if (expr.right().type().isIntegral() + || expr.right().type().equals(ClassHierarchy.POS_SHORT) + || expr.right().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INTEGER); + + } 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.INTEGER); + + } 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(final NegExpr expr) { + if (expr.expr().type().isIntegral() + || expr.expr().type().equals(ClassHierarchy.POS_SHORT) + || expr.expr().type().equals(ClassHierarchy.POS_BYTE)) { + + start(expr, Type.INTEGER); + + } else { + prop(expr, expr.expr()); + } + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + start(expr, Type.ADDRESS); + } + + public void visitCheckExpr(final CheckExpr expr) { + prop(expr, expr.expr()); + } + + // Why does it start the expression with an INTEGER type??? + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + start(expr, Type.INTEGER); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + start(expr, Type.INTEGER); + } + + // 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(final 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(final StackManipStmt stmt) { + // a := b + // [b] <= [a] + + final StackExpr[] target = stmt.target(); + final 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(final StackExpr[] source, final StackExpr[] target, + final 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(final 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(final 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(final PhiStmt stmt) { + // a := Phi(b, c) + // [b] <= [a] + // [c] <= [a] + + final List back = new ArrayList(stmt.operands().size()); + + Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + final 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()) { + final 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(final ArrayRefExpr expr) { + // a[i] + // [a[i]] <= [element type of a] + + final Expr array = expr.array(); + if (!expr.isDef()) { + if (!array.type().equals(TypeInference.UNDEF) + && !array.type().equals(Type.OBJECT) + && !array.type().equals(Type.SERIALIZABLE) + && !array.type().equals(Type.CLONEABLE) + && !array.type().isNull()) { + + Assert.isTrue(array.type().isArray(), array + " in " + expr + + " (" + array.type() + ") is not an array"); + start(expr, expr.array().type().elementType()); + } + } + } + + // The return type of the method flow into the CallMethodExpr. + public void visitCallMethodExpr(final CallMethodExpr expr) { + // x.m(a), m in class C + // [x] <= C + // [a] <= [declared param type] + // [declared return type] <= [x.m(a)] + + final MemberRef method = expr.method(); + + final Type returnType = method.type().returnType(); + + start(expr, returnType); + } + + // The return type of the method flows into the CallStaticExpr. + public void visitCallStaticExpr(final CallStaticExpr expr) { + // m(a) + // [a] <= [declared param type] + // [declared return type] <= [m(a)] + + final MemberRef method = expr.method(); + + final Type returnType = method.type().returnType(); + + start(expr, returnType); + } + + // The type to which an expression is cast flows into the CastExpr. + public void visitCastExpr(final 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(final ConstantExpr expr) { + // "a" + // String <= ["a"] + + final 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.INTEGER); + 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.CHARACTER); + + } else { + start(expr, Type.INTEGER); + } + + } 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.INTEGER); + } + } + } + + // If the FieldExpr is a definition, the type of the field flows + // into the type of the FieldExpr. + public void visitFieldExpr(final FieldExpr expr) { + // a.f, f in class C + // [a] <= C + // [declared field type] <= [a.f] + + final 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(final NewArrayExpr expr) { + // new t[i] + // array-1-of-t <= [new t[i]] + + if (!expr.elementType().equals(TypeInference.UNDEF)) { + start(expr, expr.elementType().arrayType()); + } + } + + // The type of the object being created flows into the NewExpr. + public void visitNewExpr(final 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(final NewMultiArrayExpr expr) { + // new t[i][j][k] + // array-dim-of-t <= [new t[i][j][k]] + + if (!expr.elementType().equals(TypeInference.UNDEF)) { + start(expr, expr.elementType().arrayType(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(final StaticFieldExpr expr) { + // C.f + // [declared field type] <= [C.f] + + final 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(final 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 (!type.isIntegral() && !type.equals(ClassHierarchy.POS_BYTE) + && !type.equals(ClassHierarchy.POS_SHORT)) { + Assert.isTrue(type.simple().equals(expr.type().simple())); + + if (type.isReference()) { + // 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. + final BitSet v1 = ClassHierarchy.typeToSet(type); + final 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. + final Iterator iter = ssaGraph.equivalent(expr).iterator(); + + while (iter.hasNext()) { + final Node node = (Node) iter.next(); + + if (node instanceof Expr) { + final 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(final Expr expr, final 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/tools/.cvsignore b/src/EDU/purdue/cs/bloat/tools/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tools/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/tools/BloatBenchmark.java b/src/EDU/purdue/cs/bloat/tools/BloatBenchmark.java new file mode 100644 index 0000000..8cc6713 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tools/BloatBenchmark.java @@ -0,0 +1,954 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tools; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.benchmark.*; +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.codegen.*; +import EDU.purdue.cs.bloat.context.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.file.*; +import EDU.purdue.cs.bloat.inline.*; +import EDU.purdue.cs.bloat.optimize.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.trans.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * This program is used to BLOAT Java programs. In particular, we use it to + * BLOAT the programs used to benchmark BLOAT. This program is intended to be + * run multiple times to generate multiple BLOATed programs. + */ +public class BloatBenchmark { + public static boolean TRACE = false; + + private static boolean INLINE = false; + + private static boolean INTRA = false; + + private static boolean PEEPHOLE = false; + + private static boolean VERIFY = true; + + private static boolean SPECIALIZE = false; + + private static boolean SUN = false; + + private static boolean USE1_1 = false; + + private static boolean CHECK = true; + + private static boolean TIMES = false; + + private static final PrintWriter err = new PrintWriter(System.err, true); + + private static final Set CLASSES = new HashSet(); + + private static String statsFile = null; + + private static String timesFile = null; + + private static int DEPTH = 2; // No. calls deep + + private static int SIZE = 1000; // Max size of methods + + private static int MORPH = -1; // Max "morphosity" of virtual calls + + private static int CALLEE_SIZE = -1; + + private static final List SKIP = new ArrayList(); // Classes that are + // specifically not + // optimized + + private static void tr(final String s) { + if (BloatBenchmark.TRACE) { + System.out.println(s); + } + } + + private static void usage() { + BloatBenchmark.err + .println("java TestSpecialize [options] classNames outputDir"); + BloatBenchmark.err.println("where [options] are:"); + BloatBenchmark.err + .println(" -calleeSize size Max method size to inline"); + BloatBenchmark.err + .println(" -classpath path Classpath is always prepended"); + BloatBenchmark.err.println(" -depth depth Max inline depth"); + BloatBenchmark.err + .println(" -inline Inline calls to static methods"); + BloatBenchmark.err + .println(" -intra Intraprocedural BLOAT"); + BloatBenchmark.err + .println(" -lookIn dir Look for classes here"); + BloatBenchmark.err + .println(" -morph morph Max morphosity of call sites"); + BloatBenchmark.err.println(" -no-verify Don't verify CFG"); + BloatBenchmark.err + .println(" -no-opt-stack Don't optimize stack usage"); + BloatBenchmark.err + .println(" -no-stack-vars Don't use stack vars in CFG"); + BloatBenchmark.err + .println(" -no-stack-alloc Don't try to push locals onto the operand stack"); + BloatBenchmark.err + .println(" -peel-loops " + + "\n Peel innermost loops to enable code hoisting" + + "\n (n >= 0 is the maximum loop level to peel)"); + BloatBenchmark.err + .println(" -no-pre Don't perform partial redundency elimination"); + BloatBenchmark.err + .println(" -no-dce Don't perform dead code elimination"); + BloatBenchmark.err + .println(" -no-prop Don't perform copy and constant propagation"); + BloatBenchmark.err + .println(" -no-color Don't do graph coloring"); + BloatBenchmark.err + .println(" -peephole Perform peephole after inter"); + BloatBenchmark.err.println(" -size size Max method size"); + BloatBenchmark.err + .println(" -specialize Specialize virtual method calls"); + BloatBenchmark.err.println(" -stats statsFile Generate stats"); + BloatBenchmark.err.println(" -sun Include sun packages"); + BloatBenchmark.err.println(" -times timesFile Print timings"); + BloatBenchmark.err + .println(" -trace Print trace information"); + BloatBenchmark.err + .println(" -no-check Don't check that my options 'make sense'"); + BloatBenchmark.err.println(" -skip " + + "\n Skip the given class or package"); + BloatBenchmark.err.println(" -1.1 BLOAT for JDK1.1"); + BloatBenchmark.err.println(" -1.2 BLOAT for JDK1.2"); + BloatBenchmark.err.println(""); + System.exit(1); + } + + public static void main(final String[] args) { + String CLASSPATH = null; + String outputDirName = null; + String lookIn = null; + + // Parse the command line + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-trace")) { + BloatBenchmark.TRACE = true; + PersistentBloatContext.DB_COMMIT = true; + + } else if (args[i].equals("-calleeSize")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No callee size specified"); + BloatBenchmark.usage(); + } + + try { + BloatBenchmark.CALLEE_SIZE = Integer.parseInt(args[i]); + + } catch (final NumberFormatException ex33) { + BloatBenchmark.err.println("** Bad number: " + args[i]); + BloatBenchmark.usage(); + } + + } else if (args[i].startsWith("-classpath")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No classpath specified"); + BloatBenchmark.usage(); + } + + // If there is more than one -classpath append it to the + // current one. That way the CLASSPATH reflects the order in + // which the options came on the command line. + if (CLASSPATH == null) { + CLASSPATH = args[i]; + + } else { + CLASSPATH += File.pathSeparator + args[i]; + } + + } else if (args[i].equals("-no-stack-alloc")) { + Main.STACK_ALLOC = false; + + } else if (args[i].equals("-peel-loops")) { + if (++i >= args.length) { + BloatBenchmark.usage(); + } + + final 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) { + BloatBenchmark.usage(); + } + } catch (final NumberFormatException ex) { + BloatBenchmark.usage(); + } + } + } else if (args[i].equals("-no-color")) { + Liveness.UNIQUE = true; + + } else if (args[i].equals("-no-dce")) { + Main.DCE = false; + + } else if (args[i].equals("-no-prop")) { + Main.PROP = false; + + } else if (args[i].equals("-no-pre")) { + Main.PRE = false; + + } else if (args[i].equals("-no-check")) { + BloatBenchmark.CHECK = false; + + } else if (args[i].equals("-depth")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No depth specified"); + BloatBenchmark.usage(); + } + + try { + BloatBenchmark.DEPTH = Integer.parseInt(args[i]); + + } catch (final NumberFormatException ex33) { + BloatBenchmark.err.println("** Bad number: " + args[i]); + BloatBenchmark.usage(); + } + + } else if (args[i].equals("-inline")) { + // Inline calls to static methods + BloatBenchmark.INLINE = true; + + } else if (args[i].equals("-intra")) { + BloatBenchmark.INTRA = true; + + } else if (args[i].equals("-lookIn")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No directory specified"); + BloatBenchmark.usage(); + } + + if (lookIn != null) { + lookIn += File.pathSeparator + args[i]; + + } else { + lookIn = args[i]; + } + + } else if (args[i].equals("-morph")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No morphosity specified"); + BloatBenchmark.usage(); + } + + try { + BloatBenchmark.MORPH = Integer.parseInt(args[i]); + + } catch (final NumberFormatException ex33) { + BloatBenchmark.err.println("** Bad number: " + args[i]); + BloatBenchmark.usage(); + } + + } else if (args[i].equals("-noinline")) { + // Don't perform inlining, just specialize + BloatBenchmark.INLINE = false; + + } else if (args[i].equals("-peephole")) { + // Perform peephole optimizations when doing interprocedural + // stuff + BloatBenchmark.PEEPHOLE = true; + + } else if (args[i].equals("-size")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No size specified"); + BloatBenchmark.usage(); + } + + try { + BloatBenchmark.SIZE = Integer.parseInt(args[i]); + + } catch (final NumberFormatException ex33) { + BloatBenchmark.err.println("** Bad number: " + args[i]); + BloatBenchmark.usage(); + } + + } else if (args[i].equals("-specialize")) { + // Specialize virtual method call sites + BloatBenchmark.SPECIALIZE = true; + + } else if (args[i].equals("-stats")) { + if (++i >= args.length) { + BloatBenchmark.err.println("** No stats file specified"); + BloatBenchmark.usage(); + } + + BloatBenchmark.statsFile = args[i]; + + } else if (args[i].equals("-sun")) { + // Optimize sun packages + BloatBenchmark.SUN = true; + + } else if (args[i].equals("-times")) { + BloatBenchmark.TIMES = true; + + if (++i >= args.length) { + BloatBenchmark.err.println("** No times file specified"); + BloatBenchmark.usage(); + } + + BloatBenchmark.timesFile = args[i]; + + } else if (args[i].equals("-no-verify")) { + BloatBenchmark.VERIFY = false; + + } else if (args[i].equals("-no-opt-stack")) { + CodeGenerator.OPT_STACK = false; + + } else if (args[i].equals("-no-stack-vars")) { + Tree.USE_STACK = false; + + } else if (args[i].equals("-skip")) { + if (++i >= args.length) { + BloatBenchmark.usage(); + } + + String pkg = args[i]; + + // Account for class file name on command line + if (pkg.endsWith(".class")) { + pkg = pkg.substring(0, pkg.lastIndexOf('.')); + } + + BloatBenchmark.SKIP.add(pkg.replace('.', '/')); + + } else if (args[i].equals("-1.1")) { + // There are some classes that we don't want to be pre-live. + // They don't exist in JDK1.1. + BloatBenchmark.USE1_1 = true; + CallGraph.USE1_2 = false; + + } else if (args[i].equals("-1.2")) { + CallGraph.USE1_2 = true; + + if (lookIn != null) { + lookIn += File.separator + "1.2"; + } + + } else if (args[i].startsWith("-")) { + BloatBenchmark.err + .println("** Unrecognized option: " + args[i]); + BloatBenchmark.usage(); + + } else if (i == args.length - 1) { + outputDirName = args[i]; + + } else { + BloatBenchmark.CLASSES.add(args[i]); + } + } + + if (BloatBenchmark.CLASSES.isEmpty()) { + BloatBenchmark.err.println("** No classes specified"); + BloatBenchmark.usage(); + } + + if (outputDirName == null) { + BloatBenchmark.err.println("** No output directory specified"); + BloatBenchmark.usage(); + } + + // Make sure the options the user entered make sense + if (BloatBenchmark.CHECK) { + BloatBenchmark.checkOptions(); + } + + if (BloatBenchmark.USE1_1) { + // Don't generate stats for 1.1 + BloatBenchmark.statsFile = null; + } + + if (lookIn != null) { + CLASSPATH = lookIn + File.pathSeparator + CLASSPATH; + } + + final StringBuffer sb = new StringBuffer(); + for (int i = 0; i < args.length; i++) { + sb.append(args[i] + " "); + } + BloatBenchmark.tr("BLOATing with command line: " + sb); + + BloatContext context = null; + + float systemStart = 0.0F; + float systemDelta = 0.0F; + float systemEnd = 0.0F; + float systemTotal = 0.0F; + + float userStart = 0.0F; + float userDelta = 0.0F; + float userEnd = 0.0F; + float userTotal = 0.0F; + + PrintWriter times = null; + + if (BloatBenchmark.TIMES) { + try { + times = new PrintWriter( + new FileWriter(BloatBenchmark.timesFile), true); + + } catch (final IOException ex) { + times = new PrintWriter(System.out, true); + } + } + + if (BloatBenchmark.INTRA) { + BloatBenchmark.tr("Intraprocedural BLOAT"); + + // First compute the roots of the call graph. Figure out which + // methods are live. + context = BloatBenchmark.makeContext(CLASSPATH, null); + final Collection liveMethods = BloatBenchmark.liveMethods( + BloatBenchmark.CLASSES, context); + + // Run intraprocedural BLOAT on the live methods. + BloatBenchmark.tr(liveMethods.size() + " live methods"); + context = BloatBenchmark.makeContext(CLASSPATH, outputDirName); + BloatBenchmark.intraBloat(liveMethods, context); + + } else { + BloatBenchmark.tr("Interprocedural BLOAT"); + + if (BloatBenchmark.TIMES) { + Times.snapshot(); + systemStart = Times.systemTime(); + userStart = Times.userTime(); + } + + // Do the interprocedural BLOATing + context = BloatBenchmark.makeContext(CLASSPATH, outputDirName); + BloatBenchmark.liveMethods(BloatBenchmark.CLASSES, context); + + if (BloatBenchmark.TIMES) { + // Take a measurement + Times.snapshot(); + + systemEnd = Times.systemTime(); + userEnd = Times.userTime(); + + systemDelta = systemEnd - systemStart; + userDelta = userEnd - userStart; + + systemStart = systemEnd; + userStart = userEnd; + + systemTotal += systemDelta; + userTotal += userDelta; + + times.println("Call graph construction"); + times.println(" User: " + userDelta); + times.println(" System: " + systemDelta); + } + + if (BloatBenchmark.SPECIALIZE) { + BloatBenchmark.specialize(context); + } + + if (BloatBenchmark.TIMES) { + // Take a measurement + Times.snapshot(); + + systemEnd = Times.systemTime(); + userEnd = Times.userTime(); + + systemDelta = systemEnd - systemStart; + userDelta = userEnd - userStart; + + systemStart = systemEnd; + userStart = userEnd; + + systemTotal += systemDelta; + userTotal += userDelta; + + times.println("Call site specialization"); + times.println(" User: " + userDelta); + times.println(" System: " + systemDelta); + } + + if (BloatBenchmark.INLINE) { + BloatBenchmark.inline(context); + } + + if (BloatBenchmark.TIMES) { + // Take a measurement + Times.snapshot(); + + systemEnd = Times.systemTime(); + userEnd = Times.userTime(); + + systemDelta = systemEnd - systemStart; + userDelta = userEnd - userStart; + + systemStart = systemEnd; + userStart = userEnd; + + systemTotal += systemDelta; + userTotal += userDelta; + + times.println("Method inlining"); + times.println(" User: " + userDelta); + times.println(" System: " + systemDelta); + } + + if (BloatBenchmark.PEEPHOLE) { + BloatBenchmark.peephole(context); + } + } + + // Commit dirty data + BloatBenchmark.tr("Committing dirty methods"); + context.commitDirty(); + + if (BloatBenchmark.TIMES) { + // Take a measurement + Times.snapshot(); + + systemEnd = Times.systemTime(); + userEnd = Times.userTime(); + + systemDelta = systemEnd - systemStart; + userDelta = userEnd - userStart; + + systemStart = systemEnd; + userStart = userEnd; + + systemTotal += systemDelta; + userTotal += userDelta; + + times.println("Committal"); + times.println(" User: " + userDelta); + times.println(" System: " + systemDelta); + } + + if (BloatBenchmark.TIMES) { + times.println("Total"); + times.println(" User: " + userTotal); + times.println(" System: " + systemTotal); + } + + if (BloatBenchmark.statsFile != null) { + final InlineStats stats = context.getInlineStats(); + PrintWriter statsOut = null; + try { + statsOut = new PrintWriter(new FileWriter( + BloatBenchmark.statsFile), true); + + } catch (final IOException ex) { + statsOut = new PrintWriter(System.out, true); + } + + stats.printSummary(statsOut); + } + + BloatBenchmark.tr("Finished"); + } + + /** + * Creates a BloatContext that loads classes from a given + * CLASSPATH. + */ + static BloatContext makeContext(final String classpath, + final String outputDirName) { + final ClassFileLoader loader = new ClassFileLoader(); + if (classpath != null) { + loader.prependClassPath(classpath); + } + + // if(TRACE) { + // loader.setVerbose(true); + // } + + BloatBenchmark.tr(" Creating a BloatContext for CLASSPATH: " + + loader.getClassPath()); + + if (outputDirName != null) { + loader.setOutputDir(new File(outputDirName)); + } + final BloatContext context = new CachingBloatContext(loader, + BloatBenchmark.CLASSES, true); + + // Always ignore the sun packages and the opj stuff for + // interprocedural stuff + if (!BloatBenchmark.SUN) { + context.addIgnorePackage("sun"); + } + + context.addIgnorePackage("java.lang.ref"); + context.addIgnorePackage("org.opj.system"); + + if (BloatBenchmark.USE1_1) { + // Toba can't deal with java.lang.Character + context.addIgnoreClass(Type.getType("Ljava/lang/Character;")); + } + + return (context); + } + + /** + * Returns the live methods of a program whose root methods are the + * main method of a set of classes. + * + * @param classes + * Names of classes containing root methods + * @param context + * Repository for accessing BLOAT stuff + * @return The MemberRefs of the live methods + */ + private static Collection liveMethods(final Collection classes, + final BloatContext context) { + + // Determine the roots of the call graph + final Set roots = new HashSet(); + Iterator iter = classes.iterator(); + while (iter.hasNext()) { + final String className = (String) iter.next(); + try { + final ClassEditor ce = context.editClass(className); + final MethodInfo[] methods = ce.methods(); + + for (int i = 0; i < methods.length; i++) { + final MethodEditor me = context.editMethod(methods[i]); + + if (!me.name().equals("main")) { + continue; + } + + BloatBenchmark.tr(" Root " + ce.name() + "." + me.name() + + me.type()); + roots.add(me.memberRef()); + } + + } catch (final ClassNotFoundException ex1) { + BloatBenchmark.err.println("** Could not find class: " + + ex1.getMessage()); + System.exit(1); + } + } + + if (roots.isEmpty()) { + BloatBenchmark.err.print("** No main method found in classes: "); + iter = classes.iterator(); + while (iter.hasNext()) { + final String name = (String) iter.next(); + BloatBenchmark.err.print(name); + if (iter.hasNext()) { + BloatBenchmark.err.print(", "); + } + } + BloatBenchmark.err.println(""); + } + + context.setRootMethods(roots); + final CallGraph cg = context.getCallGraph(); + + final Set liveMethods = new TreeSet(new MemberRefComparator()); + liveMethods.addAll(cg.liveMethods()); + + return (liveMethods); + } + + /** + * Specializes the live methods in a program. + */ + private static void specialize(final BloatContext context) { + + final CallGraph cg = context.getCallGraph(); + + final Set liveMethods = new TreeSet(new MemberRefComparator()); + liveMethods.addAll(cg.liveMethods()); + + // Specialize all possible methods + final InlineStats stats = context.getInlineStats(); + + if (BloatBenchmark.statsFile != null) { + Specialize.STATS = true; + stats.setConfigName("BloatBenchmark"); + } + + if (BloatBenchmark.MORPH != -1) { + Specialize.MAX_MORPH = BloatBenchmark.MORPH; + } + final Specialize spec = new Specialize(context); + + if (Specialize.STATS) { + stats.noteLiveMethods(liveMethods.size()); + stats.noteLiveClasses(cg.liveClasses().size()); + } + + BloatBenchmark.tr("Specializing live methods"); + final Iterator iter = liveMethods.iterator(); + + for (int count = 0; iter.hasNext(); count++) { + try { + final MethodEditor live = context.editMethod((MemberRef) iter + .next()); + + if (context.ignoreMethod(live.memberRef())) { + // Don't display ignored methods, it's misleading. + continue; + } + + BloatBenchmark.tr(" " + count + ") " + + live.declaringClass().name() + "." + live.name() + + live.type()); + + spec.specialize(live); + + } catch (final NoSuchMethodException ex2) { + BloatBenchmark.err.println("** Could not find method " + + ex2.getMessage()); + System.exit(1); + } + } + } + + /** + * Inlines calls to static methods in the live methods of a given program. + */ + private static void inline(final BloatContext context) { + + final Set liveMethods = new TreeSet(new MemberRefComparator()); + final CallGraph cg = context.getCallGraph(); + liveMethods.addAll(cg.liveMethods()); + + BloatBenchmark.tr("Inlining " + liveMethods.size() + " live methods"); + + if (BloatBenchmark.CALLEE_SIZE != -1) { + Inline.CALLEE_SIZE = BloatBenchmark.CALLEE_SIZE; + } + + final Iterator iter = liveMethods.iterator(); + for (int count = 0; BloatBenchmark.INLINE && iter.hasNext(); count++) { + try { + final MethodEditor live = context.editMethod((MemberRef) iter + .next()); + + if (context.ignoreMethod(live.memberRef())) { + // Don't display ignored methods, it's misleading. + continue; + } + + BloatBenchmark.tr(" " + count + ") " + + live.declaringClass().name() + "." + live.name() + + live.type()); + + final Inline inline = new Inline(context, BloatBenchmark.SIZE); + inline.setMaxCallDepth(BloatBenchmark.DEPTH); + inline.inline(live); + + // Commit here in an attempt to conserve memory + context.commit(live.methodInfo()); + context.release(live.methodInfo()); + + } catch (final NoSuchMethodException ex3) { + BloatBenchmark.err.println("** Could not find method " + + ex3.getMessage()); + System.exit(1); + } + } + } + + /** + * Performs peephole optimizations on a program's live methods. + */ + private static void peephole(final BloatContext context) { + + final Set liveMethods = new TreeSet(new MemberRefComparator()); + final CallGraph cg = context.getCallGraph(); + liveMethods.addAll(cg.liveMethods()); + + // Perform peephole optimizations. We do this separately because + // some peephole optimizations do things to the stack that + // inlining doesn't like. For instance, a peephole optimizations + // might make it so that a method has a non-empty stack upon + // return. Inlining will barf at the sight of this. + BloatBenchmark.tr("Performing peephole optimizations"); + + final Iterator iter = liveMethods.iterator(); + while (BloatBenchmark.PEEPHOLE && iter.hasNext()) { + try { + final MethodEditor live = context.editMethod((MemberRef) iter + .next()); + Peephole.transform(live); + context.commit(live.methodInfo()); + context.release(live.methodInfo()); + + } catch (final NoSuchMethodException ex314) { + BloatBenchmark.err.println("** Could not find method " + + ex314.getMessage()); + ex314.printStackTrace(System.err); + System.exit(1); + } + } + } + + /** + * Performs intraprocedural BLOAT on a program's live methods. + * + * @param liveMethods + * Should be alphabetized. This way we can commit a class once + * we've BLOATed all of its methods. + */ + private static void intraBloat(final Collection liveMethods, + final BloatContext context) { + + ClassEditor prevClass = null; + final Iterator iter = liveMethods.iterator(); + for (int count = 0; iter.hasNext(); count++) { + MethodEditor live = null; + ClassEditor ce = null; // Hack to make sure commit happens + try { + live = context.editMethod((MemberRef) iter.next()); + ce = context.editClass(live.declaringClass().classInfo()); + + } catch (final NoSuchMethodException ex3) { + BloatBenchmark.err.println("** Could not find method " + + ex3.getMessage()); + System.exit(1); + } + + /* So we can skip classes or packages */ + final String name = ce.type().className(); + final String qual = ce.type().qualifier() + "/*"; + boolean skip = false; + for (int i = 0; i < BloatBenchmark.SKIP.size(); i++) { + final String pkg = (String) BloatBenchmark.SKIP.get(i); + + if (name.equals(pkg) || qual.equals(pkg)) { + skip = true; + break; + } + } + + if (context.ignoreMethod(live.memberRef()) || skip) { + // Don't display ignored methods, it's misleading. + context.release(live.methodInfo()); + continue; + } + + final Runtime runtime = Runtime.getRuntime(); + runtime.gc(); + + final Date start = new Date(); + BloatBenchmark.tr(" " + count + ") " + + live.declaringClass().name() + "." + live.name() + + live.type()); + BloatBenchmark.tr(" Start: " + start); + + try { + EDU.purdue.cs.bloat.optimize.Main.TRACE = BloatBenchmark.TRACE; + if (!BloatBenchmark.VERIFY) { + EDU.purdue.cs.bloat.optimize.Main.VERIFY = false; + } + EDU.purdue.cs.bloat.optimize.Main.bloatMethod(live, context); + + } catch (final Exception oops) { + BloatBenchmark.err + .println("******************************************"); + BloatBenchmark.err.println("Exception while BLOATing " + + live.declaringClass().name() + "." + live.name() + + live.type()); + BloatBenchmark.err.println(oops.getMessage()); + oops.printStackTrace(System.err); + BloatBenchmark.err + .println("******************************************"); + } + + // Commit here in an attempt to conserve memory + context.commit(live.methodInfo()); + context.release(live.methodInfo()); + + if (prevClass == null) { + prevClass = ce; + + } else if (!prevClass.equals(ce)) { + // We've finished BLOATed the methods for prevClass, commit + // prevClass and move on + BloatBenchmark.tr(prevClass.type() + " != " + ce.type()); + context.commit(prevClass.classInfo()); + context.release(prevClass.classInfo()); + // context.commitDirty(); + // tr(context.toString()); + prevClass = ce; + + } else { + context.release(ce.classInfo()); + } + + final Date end = new Date(); + BloatBenchmark.tr(" Ellapsed time: " + + (end.getTime() - start.getTime()) + " ms"); + } + + context.commitDirty(); + } + + /** + * Checks to make sure that the chosen options make sense. + */ + private static void checkOptions() { + if (!BloatBenchmark.INTRA && !BloatBenchmark.SPECIALIZE + && !BloatBenchmark.INLINE) { + BloatBenchmark.err.println("** There is nothing to do!"); + BloatBenchmark.usage(); + + } else if ((BloatBenchmark.MORPH != -1) && !BloatBenchmark.SPECIALIZE) { + BloatBenchmark.err + .println("** Must specialize when setting morphosity"); + BloatBenchmark.usage(); + } + } + + private static class MemberRefComparator implements Comparator { + public int compare(final Object o1, final Object o2) { + Assert.isTrue(o1 instanceof MemberRef, o1 + " is not a MemberRef!"); + Assert.isTrue(o2 instanceof MemberRef, o2 + " is not a MemberRef!"); + + final MemberRef me1 = (MemberRef) o1; + final MemberRef me2 = (MemberRef) o2; + + final String s1 = me1.declaringClass() + "." + me1.name() + + me1.type(); + final String s2 = me2.declaringClass() + "." + me2.name() + + me2.type(); + + return (s1.compareTo(s2)); + } + + public boolean equals(final Object other) { + return (true); + } + } +} diff --git a/src/EDU/purdue/cs/bloat/tools/Makefile b/src/EDU/purdue/cs/bloat/tools/Makefile new file mode 100644 index 0000000..3827bd9 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tools/Makefile @@ -0,0 +1,23 @@ +# 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. +# +# 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 + + +CLASS = \ + BloatBenchmark.class + +include ../class.mk diff --git a/src/EDU/purdue/cs/bloat/trans/.cvsignore b/src/EDU/purdue/cs/bloat/trans/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/.cvsignore @@ -0,0 +1 @@ +*.class \ No newline at end of file diff --git a/src/EDU/purdue/cs/bloat/trans/CompactArrayInitializer.java b/src/EDU/purdue/cs/bloat/trans/CompactArrayInitializer.java new file mode 100644 index 0000000..ec8767f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/CompactArrayInitializer.java @@ -0,0 +1,786 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * CompactArrayInitializer optimizes the initialization of arrays by + * transforming the initialization code into a loop that loads the array + * elements from a string in the class's constant pool. + */ +public class CompactArrayInitializer implements Opcode { + public static boolean DEBUG = false; + + private static final MemberRef GET_CHARS; + + // Various states that the analyzer can be in. The state indicates + // what kind of instruction the analyzer expects to see next. + private static final int EXPECT_SIZE = 0; + + private static final int EXPECT_NEW = 1; + + private static final int EXPECT_DUP = 2; + + private static final int EXPECT_INDEX_OR_SIZE = 3; + + private static final int EXPECT_VALUE_OR_SIZE_OR_NEW = 4; + + private static final int EXPECT_STORE_OR_NEW = 5; + + private static final int EXPECT_PUT_OR_DUP = 6; + + private static final int THRESHOLD = 16; + + private static final String[] STATES = { "EXPECT_SIZE", "EXPECT_NEW", + "EXPECT_DUP", "EXPECT_INDEX_OR_SIZE", + "EXPECT_VALUE_OR_SIZE_OR_NEW", "EXPECT_STORE_OR_NEW", + "EXPECT_PUT_OR_DUP" }; + + static { + + // void String.getChars(int srcBegin, int scrEnd, char dst[], + // int dstBegin); + // Copies characters from a String object into a char array. + + GET_CHARS = new MemberRef(Type.STRING, new NameAndType("getChars", Type + .getType("(II[CI)V"))); + } + + /** + * Some Java compilers initialize arrays using straight-line code. For + * classes that have large, initialized arrays this results in unnecessarily + * large classfiles. CompactArrayInitializer examines a method + * (via its MethodEditor) creates a string in the method's + * class's constant pool that contains all of the elements of the + * initialized array. After the old initialization code is removed from the + * method, new code is inserted that essentially is a loop that loads each + * element from the string into the array. Note that only arrays of + * int, short, char, byte, + * and boolean are compacted. + * + * @param method + * The method whose array initializations are to be compacted. + */ + + public static boolean transform(final MethodEditor method) { + if (CompactArrayInitializer.DEBUG) { + System.out.println("Compacting array initializer in " + method); + } + + boolean filled = false; // Was the constant string generated and + // entered? + + int state = CompactArrayInitializer.EXPECT_SIZE; // The state that we + // are currently in + + int size = 0; // Size of the array + int value = 0; // A value in the array + int index = 0; // Current index into the array + int[] data = null; // Contents of the array whose initialization is + // being optimized. + Type elementType = null; // Of what Type is the array? + + // Keep track of all the Instructions and Labels that deal with array + // initialization. + final ArrayList2 buf = new ArrayList2(method.code().size()); + + // Get the code (Labels and Instructions) for the method we're editing + final Iterator iter = method.code().iterator(); + + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (CompactArrayInitializer.DEBUG) { + System.out.println("Examining " + ce); + // if (false) { + System.out.println("state = " + + CompactArrayInitializer.STATES[state]); + // } + } + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + switch (state) { + case EXPECT_SIZE: + switch (inst.opcodeClass()) { + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + size = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_NEW; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_NEW: + switch (inst.opcodeClass()) { + case opcx_newarray: + elementType = (Type) inst.operand(); + + if (elementType.isIntegral()) { + data = new int[size]; + state = CompactArrayInitializer.EXPECT_DUP; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + size = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_NEW; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_DUP: + switch (inst.opcodeClass()) { + case opcx_dup: + state = CompactArrayInitializer.EXPECT_INDEX_OR_SIZE; + break; + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + size = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_NEW; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_INDEX_OR_SIZE: + switch (inst.opcodeClass()) { + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + + index = ((Number) inst.operand()).intValue(); + + if (index < data.length) { + state = CompactArrayInitializer.EXPECT_VALUE_OR_SIZE_OR_NEW; + } else { + // Out of range. Can't be an index, + // so assume it's a size. + size = index; + state = CompactArrayInitializer.EXPECT_NEW; + } + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_VALUE_OR_SIZE_OR_NEW: + switch (inst.opcodeClass()) { + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + + value = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_STORE_OR_NEW; + } else if (inst.operand() instanceof Character) { + final Character ch = (Character) inst.operand(); + value = ch.charValue(); + state = CompactArrayInitializer.EXPECT_STORE_OR_NEW; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + case opcx_newarray: + size = index; + elementType = (Type) inst.operand(); + + if (elementType.isIntegral()) { + data = new int[size]; + state = CompactArrayInitializer.EXPECT_DUP; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_STORE_OR_NEW: + switch (inst.opcodeClass()) { + case opcx_bastore: + if (elementType.equals(Type.BYTE) + || elementType.equals(Type.BOOLEAN)) { + data[index] = value; + state = CompactArrayInitializer.EXPECT_PUT_OR_DUP; + } + break; + case opcx_castore: + if (elementType.equals(Type.CHARACTER)) { + data[index] = value; + state = CompactArrayInitializer.EXPECT_PUT_OR_DUP; + } + break; + case opcx_sastore: + if (elementType.equals(Type.SHORT)) { + data[index] = value; + state = CompactArrayInitializer.EXPECT_PUT_OR_DUP; + } + break; + case opcx_iastore: + if (elementType.equals(Type.INTEGER)) { + data[index] = value; + state = CompactArrayInitializer.EXPECT_PUT_OR_DUP; + } + break; + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + + size = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_NEW; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + case opcx_newarray: + size = value; + elementType = (Type) inst.operand(); + + if (elementType.isIntegral()) { + data = new int[size]; + state = CompactArrayInitializer.EXPECT_DUP; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + case EXPECT_PUT_OR_DUP: + switch (inst.opcodeClass()) { + case opcx_dup: + state = CompactArrayInitializer.EXPECT_INDEX_OR_SIZE; + break; + case opcx_ldc: + if ((inst.operand() instanceof Byte) + || (inst.operand() instanceof Short) + || (inst.operand() instanceof Integer)) { + size = ((Number) inst.operand()).intValue(); + state = CompactArrayInitializer.EXPECT_NEW; + } else { + state = CompactArrayInitializer.EXPECT_SIZE; + } + break; + case opcx_astore: + case opcx_aastore: + case opcx_putstatic: + case opcx_putstatic_nowb: + case opcx_putfield: + case opcx_putfield_nowb: + if (data.length >= CompactArrayInitializer.THRESHOLD) { + CompactArrayInitializer.fillArray(method, buf, + elementType, data); + filled = true; + } + state = CompactArrayInitializer.EXPECT_SIZE; + break; + default: + state = CompactArrayInitializer.EXPECT_SIZE; + break; + } + break; + } + } else { + final Label label = (Label) ce; + + if (label.startsBlock()) { + state = CompactArrayInitializer.EXPECT_SIZE; + } + } + + if (CompactArrayInitializer.DEBUG /* && false */) { + System.out.println(" -> " + + CompactArrayInitializer.STATES[state]); + } + + buf.add(ce); + } + + if (filled) { + method.code().clear(); + method.code().addAll(buf); + + if (CompactArrayInitializer.DEBUG) { + for (int i = 0; i < method.code().size(); i++) { + System.out.println("code[" + i + "] " + + method.code().get(i)); + } + } + } + + return filled; + } + + /** + * Construct a UTF8 string that stores the contents of an integral (int, + * char, or byte/boolean) array. Each element of the UTF8 string is 16 bits + * wide meaning that an int is stored into two elements, a char is store in + * one element, and two bytes/booleans are stored in one element. + * Essentially each integer in the data parmeter is converted + * into a character and placed in an array. + *

+ * A UTF8 string cannot be larger than 64K. To be on the safe side, we do + * not generate UTF8 strings that are larger than 32K. + *

+ * We then remove all of the old initialization code, so that the previous + * instruction is a newarray opcode (meaning that the array + * object will be on top of the stack). + *

+ * Then new code is generated for loading data from the UTF8 string. Loading + * character data is relatively straightforward. The getChars + * method is invoked on the UTF8 string (an instance of String) + * to copy characters from the string into a character array. + *

+ * A little more work has to be done for non-character data types. First, + * the UTF8 string is read into a (local) character array. Then, the data is + * extracted from the character array and placed in the new array. For some + * data types, labels are added to the program. + * + * @param m + * The method that contains the array + * @param buf + * Instructions from the method that are used in array + * initialization + * @param elementType + * The Type of the array + * @param data + * The contents of the array + * + */ + private static void fillArray(final MethodEditor m, final ArrayList2 buf, + final Type elementType, final int[] data) { + // Max UTF8 constant size is 65535 bytes. We divide this in 2 to + // prevent DataOutputStream from crashing. Since our arrays can be + // longer than 32767, we break the image into segments. + + char[] c; // string that will be entered into the constant pool + + if (elementType.equals(Type.CHARACTER)) { + // Fill the string with char data (16-bits). Each char in string + // holds a single char. + c = new char[data.length]; + + for (int i = 0; i < data.length; i++) { + c[i] = (char) data[i]; + } + } else if (elementType.equals(Type.BYTE) + || elementType.equals(Type.BOOLEAN)) { + // Fill the string with 8-bit data. Each char in string holds + // two 8-bit data. + c = new char[(data.length + 1) / 2]; + + int j = 0; + + for (int i = 0; i + 1 < data.length; i += 2) { + c[j++] = (char) ((data[i] << 8) | (data[i + 1] & 0xff)); + } + + if (j != c.length) { + c[j++] = (char) (data[data.length - 1] << 8); + } + } else if (elementType.equals(Type.SHORT)) { + // Fill the string with short (16-bit) data. I don't know why we + // add 0x8000 to it, but we subtract 32768 (0x8000) in the + // generated byetcode. Mysteries of BLOAT... + + c = new char[data.length]; + + for (int i = 0; i < data.length; i++) { + c[i] = (char) (data[i] + 0x8000); + } + } else if (elementType.equals(Type.INTEGER)) { + // Fill the string with int (32-bit) data. ints are stored as + // chars in big-endian format + c = new char[data.length * 2]; + + int j = 0; + + for (int i = 0; i < data.length; i++) { + final int n = data[i]; + c[j++] = (char) ((n >>> 16) & 0xffff); + c[j++] = (char) ((n >>> 0) & 0xffff); + } + } else { + return; + } + + // The Strings of data divided into 32K chunks. Each chunk is an + // element in the ArrayList + final ArrayList image = new ArrayList(); + + // The start index in the array for each segment of the image. + final ArrayList startIndex = new ArrayList(); + + // The end index+1 in the array for each segment of the image. + final ArrayList endIndex = new ArrayList(); + + StringBuffer sb = new StringBuffer(); + int utfLength = 0; + startIndex.add(new Integer(0)); + + // Iterate over every character in the array buffer. Use a + // StringBuffer to create String of length less than 32K. + for (int i = 0; i < c.length; i++) { + final char n = c[i]; + int len = 0; + + if (n == '\u0000') { + len = 2; + } else if (n < '\u0800') { + len = 1; + } else if (n < '\u8000') { + len = 2; + } else { + len = 3; + } + + if (utfLength + len > 32767) { + // We've reached the limit on the size of the constant pool + // string. + // Add the current string buffer, and make a new one. + image.add(sb.toString()); + endIndex.add(new Integer(i)); + + sb = new StringBuffer(); + utfLength = 0; + startIndex.add(new Integer(i)); + } + + sb.append(n); + utfLength += len; + } + + if (sb.length() > 0) { + // If we've got leftovers, add it to the end of the current image + // entry. + image.add(sb.toString()); + endIndex.add(new Integer(data.length)); + } else { + startIndex.remove(startIndex.size() - 1); + } + + int bufStart = -1; + + // Remove the old code, leaving just the creation of the array!!!!! + + for (int i = buf.size() - 1; i >= 0; i--) { + final Instruction inst = (Instruction) buf.get(i); + if (inst.opcodeClass() == Opcode.opcx_newarray) { + // ..., ldc, new, dup ldc ldc store, dup ldc ldc store, ... + buf.removeRange(i + 1, buf.size()); + bufStart = i; + break; + } + } + + if (bufStart == -1) { + // There was no code to remove? Something went wrong. Run away! + return; + } + + // Insert the new: + if (elementType.equals(Type.CHARACTER)) { + // We envoke the method String.getChars() to copy characters from + // a String object (the UTF8 constant in the constant pool) to + // a character array. Remember that the destination array is on + // the top of the stack. + + final LocalVariable array = m.newLocal(Type.OBJECT); // character + // array + + buf.add(new Instruction(Opcode.opcx_dup, array)); + buf.add(new Instruction(Opcode.opcx_astore, array)); + + // Call getChars() for every image + + for (int i = 0; i < image.size(); i++) { + final String im = (String) image.get(i); + final Integer start = (Integer) startIndex.get(i); + + // void getChars(int srcBegin, int srcEnd, char dst[], + // int dstBegin) + + buf.add(new Instruction(Opcode.opcx_ldc, im)); // String + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(0))); + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(im + .length()))); + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_ldc, start)); // dstBegin + buf.add(new Instruction(Opcode.opcx_invokevirtual, + CompactArrayInitializer.GET_CHARS)); + } + + } else { + + // Loading and storing non-character data is a little more + // tricky. First we must read the UTF8 string into a character + // array. The we must go through the array and pick out the + // elements of the int, short, byte/boolean array. + + // array is a character array used to hold the UTF8 string + // index1 is an index into the destination (int, boolean, etc.) + // array and index2 is an index into the char array from the + // constant pool. tmp is a temporary char local variable that + // is used because booleans and bytes need to be left shifted. + + final LocalVariable array = m.newLocal(Type.OBJECT); // char + // array + final LocalVariable index1 = m.newLocal(Type.INTEGER); + final LocalVariable index2 = m.newLocal(Type.INTEGER); + LocalVariable tmp = null; + + if (elementType.equals(Type.BYTE) + || elementType.equals(Type.BOOLEAN)) { + tmp = m.newLocal(Type.CHARACTER); + } + + // Call getChars() to read the UTF8 string from the constant + // pool into an array of characters, array. + + for (int i = 0; i < image.size(); i++) { + final Label top = m.newLabel(); + top.setStartsBlock(true); + + final Label bottom = m.newLabel(); + bottom.setStartsBlock(true); + + final String im = (String) image.get(i); + final Integer start = (Integer) startIndex.get(i); + final Integer end = (Integer) endIndex.get(i); + + if (CompactArrayInitializer.DEBUG) { + System.out.println("image " + im); + System.out.println("start " + start); + System.out.println("end " + end); + } + + buf.add(new Instruction(Opcode.opcx_ldc, start)); + buf.add(new Instruction(Opcode.opcx_istore, index1)); + + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(0))); + buf.add(new Instruction(Opcode.opcx_istore, index2)); + + // Create a new array of characters and copy the UTF8 string + // into it. + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(im + .length()))); + buf.add(new Instruction(Opcode.opcx_newarray, Type.CHARACTER)); + buf.add(new Instruction(Opcode.opcx_astore, array)); + buf.add(new Instruction(Opcode.opcx_ldc, im)); + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(0))); + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(im + .length()))); + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(0))); + buf.add(new Instruction(Opcode.opcx_invokevirtual, + CompactArrayInitializer.GET_CHARS)); + + // Start the fill loop for [start[i], end[i]). + buf.add(top); + + // Store the image data into the data array + // (at the top of the stack). + if (elementType.equals(Type.SHORT)) { + // Load an integer from the character array and then + // subtract + // 32768 (0x8000) from it. Convert the integer to a short + // and store it in the destination array (which happens to + // be + // on the top of the stack). + + buf.add(new Instruction(Opcode.opcx_dup)); + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index1, 1))); + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_iload, index2)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index2, 1))); + buf.add(new Instruction(Opcode.opcx_caload)); + buf + .add(new Instruction(Opcode.opcx_ldc, new Integer( + 32768))); + buf.add(new Instruction(Opcode.opcx_isub)); + buf.add(new Instruction(Opcode.opcx_i2s)); + buf.add(new Instruction(Opcode.opcx_sastore)); + + } else if (elementType.equals(Type.BYTE) + || elementType.equals(Type.BOOLEAN)) { + // For byte (and boolean) arrays we need to use a temporary + // variable to hold the character because it needs to be + // shifted. Recall that two bytes were glued together into + // one character. + + // Incremenet index2 and load the character from the + // character array and store it in tmp. + // t = c[j++] + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_iload, index2)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index2, 1))); + buf.add(new Instruction(Opcode.opcx_caload)); + buf.add(new Instruction(Opcode.opcx_istore, tmp)); + + // Store the higher 8 bits of tmp into the byte array + // b[i++] = (byte) (t >>> 8) + buf.add(new Instruction(Opcode.opcx_dup)); + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index1, 1))); + buf.add(new Instruction(Opcode.opcx_iload, tmp)); + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(8))); + buf.add(new Instruction(Opcode.opcx_iushr)); + buf.add(new Instruction(Opcode.opcx_i2b)); + buf.add(new Instruction(Opcode.opcx_bastore)); + + // If we've read the last byte, go home + // if (i >= end) break + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_ldc, end)); + buf.add(new Instruction(Opcode.opcx_if_icmpge, bottom)); + + // Add a new label because we're starting a new basic + // block(?) + final Label nobreak = m.newLabel(); + nobreak.setStartsBlock(true); + buf.add(nobreak); + + // Store the lower order 8 bits of tmp into the byte array + // b[i++] = (byte) (t & 0xff) + buf.add(new Instruction(Opcode.opcx_dup)); + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index1, 1))); + buf.add(new Instruction(Opcode.opcx_iload, tmp)); + buf + .add(new Instruction(Opcode.opcx_ldc, new Integer( + 0xff))); + buf.add(new Instruction(Opcode.opcx_iand)); + buf.add(new Instruction(Opcode.opcx_i2b)); + buf.add(new Instruction(Opcode.opcx_bastore)); + + } else if (elementType.equals(Type.INTEGER)) { + // Recall that an integer is 32 bits and is therefore + // contained in two characters. So, read the first, then + // read the second. + + // Increment index1 and index2 and load the character from + // the character array. + buf.add(new Instruction(Opcode.opcx_dup)); + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index1, 1))); + + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_iload, index2)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index2, 1))); + buf.add(new Instruction(Opcode.opcx_caload)); + + // Isolate the high-order 16 bits by shifting left + buf.add(new Instruction(Opcode.opcx_ldc, new Integer(16))); + buf.add(new Instruction(Opcode.opcx_ishl)); + + // Increment index2 and obtain the character containing the + // low-order 16 bits of the integer + buf.add(new Instruction(Opcode.opcx_aload, array)); + buf.add(new Instruction(Opcode.opcx_iload, index2)); + buf.add(new Instruction(Opcode.opcx_iinc, new IncOperand( + index2, 1))); + buf.add(new Instruction(Opcode.opcx_caload)); + + // Or the higher-order bits and the lower-order bits + // together + // and store the result in the integer array. + buf.add(new Instruction(Opcode.opcx_ior)); + buf.add(new Instruction(Opcode.opcx_iastore)); + } + + // Branch back if we're not out of the loop. + // while (i < end) + buf.add(new Instruction(Opcode.opcx_iload, index1)); + buf.add(new Instruction(Opcode.opcx_ldc, end)); + buf.add(new Instruction(Opcode.opcx_if_icmplt, top)); + + buf.add(bottom); + } + } + + if (CompactArrayInitializer.DEBUG) { + for (int i = bufStart; i < buf.size(); i++) { + System.out.println("fill[" + i + "] " + buf.get(i)); + } + } + } +} + +/** + * Recall that Nate used beta versions of the JDK1.2 util classes to build + * BLOAT. While most of the conversion to the final util API was simple, there + * were a couple of changes made that force us to make some changes. + * + * The final version of the API makes the ArrayList.removeRange() method + * protected. So, we have to make this silly wrapper class in order to access + * it. Silly. + */ +class ArrayList2 extends ArrayList { + public ArrayList2(final int initialCapacity) { + super(initialCapacity); + } + + public void removeRange(final int fromIndex, final int toIndex) { + super.removeRange(fromIndex, toIndex); + } +} 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..8ad1ab7 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/DeadCodeElimination.java @@ -0,0 +1,641 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 { + public static boolean DEBUG = false; + + private static final int DEAD = 0; + + private static final int LIVE = 1; + + FlowGraph cfg; + + /** + * Constructor. + */ + public DeadCodeElimination(final FlowGraph cfg) { + this.cfg = cfg; + } + + // Keep a work list of expressions that need to be made live. + LinkedList worklist; + + /** + * Performs dead code elimination. + */ + public void transform() { + // Mark all nodes in the tree as DEAD. + cfg.visit(new TreeVisitor() { + public void visitNode(final Node node) { + node.visitChildren(this); + node.setKey(DeadCodeElimination.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(final MonitorStmt stmt) { + // NullPointerException, IllegalMonitorStateException + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitInitStmt(final InitStmt stmt) { + // Needed to correctly initialize the formal parameters when + // coloring + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitJsrStmt(final JsrStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitRetStmt(final RetStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSRStmt(final SRStmt stmt) { + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSCStmt(final SCStmt stmt) { + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitNewExpr(final NewExpr expr) { + // Memory allocation + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitStackExpr(final StackExpr expr) { + if (expr.stmt() instanceof PhiStmt) { + return; + } + + // Stack change + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + // NullPointerException or DivideByZeroException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitRCExpr(final RCExpr expr) { + // Residency check + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitUCExpr(final UCExpr expr) { + // Update check + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCastExpr(final CastExpr expr) { + // ClassCastException + if (expr.castType().isReference()) { + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitArithExpr(final ArithExpr expr) { + // DivideByZeroException + if ((expr.operation() == ArithExpr.DIV) + || (expr.operation() == ArithExpr.REM)) { + + if (expr.type().isIntegral()) { + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + return; + } + } + + expr.visitChildren(this); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + // NullPointerException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + // NullPointerException, ArrayIndexOutOfBoundsException, + // ArrayStoreException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitFieldExpr(final FieldExpr expr) { + // NullPointerException + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + // Call + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + // Call + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitCatchExpr(final CatchExpr expr) { + // Stack change + if (DeadCodeElimination.DEBUG) { + System.out.println(expr + " is prelive"); + } + + makeLive(expr); + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + // Stack change + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitThrowStmt(final ThrowStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitIfStmt(final IfStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitGotoStmt(final GotoStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitReturnStmt(final ReturnStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + // Branch + if (DeadCodeElimination.DEBUG) { + System.out.println(stmt + " is prelive"); + } + + makeLive(stmt); + } + + public void visitStoreExpr(final StoreExpr expr) { + // Can change a variable visible outside the method. + if (!(expr.target() instanceof LocalExpr)) { + if (DeadCodeElimination.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()) { + final VarExpr expr = (VarExpr) worklist.removeFirst(); + + final DefExpr def = expr.def(); + + if (def != null) { + if (DeadCodeElimination.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(final StoreExpr expr) { + final Expr lhs = expr.target(); + final Expr rhs = expr.expr(); + + if ((lhs.key() == DeadCodeElimination.DEAD) + && (rhs.key() == DeadCodeElimination.LIVE)) { + rhs.setParent(null); + expr.replaceWith(rhs, false); + + lhs.cleanup(); + expr.cleanupOnly(); + + lhs.setKey(DeadCodeElimination.DEAD); + expr.setKey(DeadCodeElimination.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(final Stmt stmt) { + if (stmt.key() == DeadCodeElimination.DEAD) { + stmt.visitChildren(this); + } + } + + public void visitExpr(final Expr expr) { + if (expr.key() == DeadCodeElimination.DEAD) { + expr.visitChildren(this); + return; + } + + final Node parent = expr.parent(); + + if (parent.key() == DeadCodeElimination.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(DeadCodeElimination.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. + + final Stmt oldStmt = expr.stmt(); + + final Tree tree = parent.block().tree(); + + // Replace the expr with an unused stack expr. + final StackExpr t = tree.newStack(expr.type()); + expr.replaceWith(t, false); + t.setValueNumber(expr.valueNumber()); + + final ExprStmt stmt = new ExprStmt(expr); + stmt.setValueNumber(expr.valueNumber()); + stmt.setKey(DeadCodeElimination.LIVE); + + tree.addStmtBefore(stmt, oldStmt); + + // The old statement is dead and will be removed later. + Assert.isTrue(oldStmt.key() == DeadCodeElimination.DEAD, + oldStmt + " should be dead"); + } + }); + + // Finally, remove the dead statements from the Tree. + cfg.visit(new TreeVisitor() { + public void visitTree(final Tree tree) { + final Iterator e = tree.stmts().iterator(); + + while (e.hasNext()) { + final Stmt stmt = (Stmt) e.next(); + + if (stmt.key() == DeadCodeElimination.DEAD) { + if (stmt instanceof LabelStmt) { + continue; + } + + if (stmt instanceof JumpStmt) { + continue; + } + + if (DeadCodeElimination.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. + + final StoreExpr expr = (StoreExpr) node; + + if (expr.key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(DeadCodeElimination.LIVE); + } + + if (expr.target().key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + expr.target() + " in " + + expr); + } + + expr.target().setKey(DeadCodeElimination.LIVE); + + if (expr.target() instanceof VarExpr) { + worklist.add(expr.target()); + } + } + + if (expr.expr().key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + expr.expr() + " in " + + expr); + } + + expr.expr().setKey(DeadCodeElimination.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. + final Node parent = ((Expr) node).parent(); + + if (parent instanceof ExprStmt) { + node = parent; + } + } + + node.visit(new TreeVisitor() { + public void visitStoreExpr(final 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(final VarExpr expr) { + if (expr.key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(DeadCodeElimination.LIVE); + worklist.add(expr); + } + } + + public void visitExpr(final Expr expr) { + if (expr.key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + expr + " in " + + expr.parent()); + } + + expr.setKey(DeadCodeElimination.LIVE); + } + + expr.visitChildren(this); + } + + public void visitStmt(final Stmt stmt) { + if (stmt.key() == DeadCodeElimination.DEAD) { + if (DeadCodeElimination.DEBUG) { + System.out.println("making live " + stmt); + } + + stmt.setKey(DeadCodeElimination.LIVE); + } + + stmt.visitChildren(this); + } + }); + } +} 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..4964992 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/ExprPropagation.java @@ -0,0 +1,261 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * Performs copy and constant propagation on the blocks in a control flow graph. + */ +public class ExprPropagation { + public static boolean DEBUG = false; + + FlowGraph cfg; + + boolean changed; // Did the cfg change? + + /** + * Constructor. + * + * @param cfg + * The control flow graph on which expression propagation is + * being performed. + */ + public ExprPropagation(final FlowGraph cfg) { + this.cfg = cfg; + } + + /** + * Performs the propagation. + */ + public void transform() { + 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(final Tree tree) { + iter = tree.stmts().iterator(); + + while (iter.hasNext()) { + final Stmt stmt = (Stmt) iter.next(); + stmt.visit(this); + } + } + + public void visitStoreExpr(final StoreExpr expr) { + expr.visitChildren(this); + + if (!(expr.target() instanceof LocalExpr)) { + // If we're not assigning to a local variable, fergit it + return; + } + + final LocalExpr lhs = (LocalExpr) expr.target(); + final 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) { + final StoreExpr store = (StoreExpr) rhs; + + final MemExpr rhsLHS = store.target(); + final 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. + final 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(final PhiStmt stmt) { + final 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. + final Iterator e = stmt.operands().iterator(); + + if (!e.hasNext()) { + return; + } + + final Expr rhs = (Expr) e.next(); + + if (!(rhs instanceof LeafExpr)) { + return; + } + + while (e.hasNext()) { + final 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(final Block block, final LocalExpr lhs, final Expr rhs) { + if (ExprPropagation.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()) { + final 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()) { + final LocalExpr use = (LocalExpr) e.next(); + use.replaceWith((Expr) rhs.clone()); + } + + return true; + + } else { + boolean replacedAll = true; + + final Iterator e = lhs.uses().iterator(); + + while (e.hasNext()) { + final LocalExpr use = (LocalExpr) e.next(); + + if (use.parent() instanceof PhiCatchStmt) { + replacedAll = false; + } else { + use.replaceWith((Expr) rhs.clone()); + } + } + + return replacedAll; + } + } +} diff --git a/src/EDU/purdue/cs/bloat/trans/Makefile b/src/EDU/purdue/cs/bloat/trans/Makefile new file mode 100644 index 0000000..e962bb8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/Makefile @@ -0,0 +1,34 @@ +# 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. +# +# 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 + + +CLASS = \ + CompactArrayInitializer.class\ + DeadCodeElimination.class\ + ExprPropagation.class\ + NodeComparator.class\ + Peephole.class\ + PersistentCheckElimination.class\ + SSAPRE.class\ + SideEffectChecker.class\ + StackPRE.class\ + ValueFolder.class\ + ValueFolding.class\ + ValueNumbering.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..36de722 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/NodeComparator.java @@ -0,0 +1,532 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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(final ExprStmt stmt) { + if (w instanceof ExprStmt) { + eq.value = true; + } + } + + public void visitIfCmpStmt(final IfCmpStmt stmt) { + if (w instanceof IfCmpStmt) { + final IfCmpStmt s = (IfCmpStmt) w; + eq.value = (stmt.trueTarget() == s.trueTarget()) + && (stmt.falseTarget() == s.falseTarget()) + && (stmt.comparison() == s.comparison()); + } + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + if (w instanceof IfZeroStmt) { + final IfZeroStmt s = (IfZeroStmt) w; + eq.value = (stmt.trueTarget() == s.trueTarget()) + && (stmt.falseTarget() == s.falseTarget()) + && (stmt.comparison() == s.comparison()); + } + } + + public void visitSCStmt(final SCStmt stmt) { + if (w instanceof SCStmt) { + final SCStmt s = (SCStmt) w; + eq.value = (stmt.array() == s.array()) + && (stmt.index() == s.index()); + } + } + + public void visitSRStmt(final SRStmt stmt) { + if (w instanceof SRStmt) { + final SRStmt s = (SRStmt) w; + eq.value = (stmt.array() == s.array()) + && (stmt.start() == s.start()) + && (stmt.end() == s.end()); + } + } + + public void visitInitStmt(final InitStmt stmt) { + if (w instanceof InitStmt) { + eq.value = true; + } + } + + public void visitGotoStmt(final GotoStmt stmt) { + if (w instanceof GotoStmt) { + final GotoStmt s = (GotoStmt) w; + eq.value = stmt.target() == s.target(); + } + } + + public void visitLabelStmt(final LabelStmt stmt) { + if (w instanceof LabelStmt) { + final LabelStmt s = (LabelStmt) w; + eq.value = stmt.label().equals(s.label()); + } + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + if (w instanceof MonitorStmt) { + final MonitorStmt s = (MonitorStmt) w; + eq.value = stmt.kind() == s.kind(); + } + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + if (w instanceof PhiJoinStmt) { + eq.value = true; + } + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + if (w instanceof PhiCatchStmt) { + eq.value = true; + } + } + + public void visitCatchExpr(final CatchExpr expr) { + // Catches are not equivalent. + eq.value = false; + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + if (w instanceof StackManipStmt) { + final StackManipStmt s = (StackManipStmt) w; + eq.value = stmt.kind() == s.kind(); + } + } + + public void visitRetStmt(final RetStmt stmt) { + if (w instanceof RetStmt) { + final RetStmt s = (RetStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + if (w instanceof ReturnExprStmt) { + eq.value = true; + } + } + + public void visitReturnStmt(final ReturnStmt stmt) { + if (w instanceof ReturnStmt) { + eq.value = true; + } + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + if (w instanceof AddressStoreStmt) { + final AddressStoreStmt s = (AddressStoreStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitStoreExpr(final StoreExpr expr) { + if (w instanceof StoreExpr) { + eq.value = true; + } + } + + public void visitJsrStmt(final JsrStmt stmt) { + if (w instanceof JsrStmt) { + final JsrStmt s = (JsrStmt) w; + eq.value = stmt.sub() == s.sub(); + } + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + if (w instanceof SwitchStmt) { + eq.value = stmt == w; + } + } + + public void visitThrowStmt(final ThrowStmt stmt) { + if (w instanceof ThrowStmt) { + eq.value = true; + } + } + + public void visitArithExpr(final ArithExpr expr) { + if (w instanceof ArithExpr) { + final ArithExpr e = (ArithExpr) w; + eq.value = e.operation() == expr.operation(); + } + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + if (w instanceof ArrayLengthExpr) { + eq.value = true; + } + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + if (w instanceof ArrayRefExpr) { + eq.value = true; + } + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + // Calls are never equal. + eq.value = false; + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + // Calls are never equal. + eq.value = false; + } + + public void visitCastExpr(final CastExpr expr) { + if (w instanceof CastExpr) { + final CastExpr e = (CastExpr) w; + eq.value = e.castType().equals(expr.castType()); + } + } + + public void visitConstantExpr(final ConstantExpr expr) { + if (w instanceof ConstantExpr) { + final ConstantExpr e = (ConstantExpr) w; + if (e.value() == null) { + eq.value = expr.value() == null; + } else { + eq.value = e.value().equals(expr.value()); + } + } + } + + public void visitFieldExpr(final FieldExpr expr) { + if (w instanceof FieldExpr) { + final FieldExpr e = (FieldExpr) w; + eq.value = e.field().equals(expr.field()); + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + if (w instanceof InstanceOfExpr) { + final InstanceOfExpr e = (InstanceOfExpr) w; + eq.value = e.checkType().equals(expr.checkType()); + } + } + + public void visitLocalExpr(final LocalExpr expr) { + if (w instanceof LocalExpr) { + final LocalExpr e = (LocalExpr) w; + eq.value = e.def() == expr.def(); + } + } + + public void visitNegExpr(final NegExpr expr) { + if (w instanceof NegExpr) { + eq.value = true; + } + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + eq.value = false; + } + + public void visitNewExpr(final NewExpr expr) { + eq.value = false; + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + eq.value = false; + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + if (w instanceof ZeroCheckExpr) { + eq.value = true; + } + } + + public void visitRCExpr(final RCExpr expr) { + if (w instanceof RCExpr) { + eq.value = true; + } + } + + public void visitUCExpr(final UCExpr expr) { + if (w instanceof UCExpr) { + final UCExpr e = (UCExpr) w; + eq.value = e.kind() == expr.kind(); + } + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + if (w instanceof ReturnAddressExpr) { + eq.value = true; + } + } + + public void visitShiftExpr(final ShiftExpr expr) { + if (w instanceof ShiftExpr) { + final ShiftExpr e = (ShiftExpr) w; + eq.value = e.dir() == expr.dir(); + } + } + + public void visitStackExpr(final StackExpr expr) { + if (w instanceof StackExpr) { + final StackExpr e = (StackExpr) w; + eq.value = e.def() == expr.def(); + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + if (w instanceof StaticFieldExpr) { + final StaticFieldExpr e = (StaticFieldExpr) w; + eq.value = e.field().equals(expr.field()); + } + } + + public void visitNode(final 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(final ExprStmt stmt) { + hash.value = 1; + } + + public void visitIfCmpStmt(final IfCmpStmt stmt) { + hash.value = stmt.comparison() + stmt.trueTarget().hashCode() + + stmt.falseTarget().hashCode(); + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + hash.value = stmt.comparison() + stmt.trueTarget().hashCode() + + stmt.falseTarget().hashCode(); + } + + public void visitInitStmt(final InitStmt stmt) { + hash.value = 2; + } + + public void visitGotoStmt(final GotoStmt stmt) { + hash.value = stmt.target().hashCode(); + } + + public void visitLabelStmt(final LabelStmt stmt) { + hash.value = stmt.label().hashCode(); + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + hash.value = stmt.kind(); + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + hash.value = 3; + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + hash.value = 4; + } + + public void visitCatchExpr(final CatchExpr expr) { + // Catches are not equivalent. + hash.value = expr.hashCode(); + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + hash.value = stmt.kind(); + } + + public void visitRetStmt(final RetStmt stmt) { + hash.value = 5; + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + hash.value = 6; + } + + public void visitReturnStmt(final ReturnStmt stmt) { + hash.value = 7; + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + hash.value = 8; + } + + public void visitStoreExpr(final StoreExpr expr) { + hash.value = 9; + } + + public void visitJsrStmt(final JsrStmt stmt) { + hash.value = 10; + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + hash.value = 11; + } + + public void visitThrowStmt(final ThrowStmt stmt) { + hash.value = 12; + } + + public void visitArithExpr(final ArithExpr expr) { + hash.value = expr.operation(); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + hash.value = 13; + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + hash.value = 14; + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + // Calls are never equal. + hash.value = expr.hashCode(); + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + // Calls are never equal. + hash.value = expr.hashCode(); + } + + public void visitCastExpr(final CastExpr expr) { + hash.value = expr.castType().hashCode(); + } + + public void visitConstantExpr(final ConstantExpr expr) { + if (expr.value() == null) { + hash.value = 0; + } else { + hash.value = expr.value().hashCode(); + } + } + + public void visitFieldExpr(final FieldExpr expr) { + hash.value = expr.field().hashCode(); + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + hash.value = expr.checkType().hashCode(); + } + + public void visitLocalExpr(final LocalExpr expr) { + if (expr.def() != null) { + hash.value = expr.def().hashCode(); + } else { + hash.value = 0; + } + } + + public void visitNegExpr(final NegExpr expr) { + hash.value = 16; + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitNewExpr(final NewExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + hash.value = expr.hashCode(); + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + hash.value = 15; + } + + public void visitRCExpr(final RCExpr expr) { + hash.value = 17; + } + + public void visitUCExpr(final UCExpr expr) { + hash.value = 18 + expr.kind(); + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + hash.value = 21; + } + + public void visitSCStmt(final SCStmt stmt) { + hash.value = 23; + } + + public void visitSRStmt(final SRStmt stmt) { + hash.value = 22; + } + + public void visitShiftExpr(final ShiftExpr expr) { + hash.value = expr.dir(); + } + + public void visitStackExpr(final StackExpr expr) { + if (expr.def() != null) { + hash.value = expr.def().hashCode(); + } else { + hash.value = 0; + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + hash.value = expr.field().hashCode(); + } + + public void visitNode(final 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..3386a06 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/Peephole.java @@ -0,0 +1,909 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Performs some peephole optimizations such as loads and stores and removes + * unreachable instructions. + */ +public class Peephole implements Opcode { + public static boolean DEBUG = false; + + /** + * 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. + */ + public static void transform(final MethodEditor method) { + if (Peephole.DEBUG) { + System.out.println("Peephole optimizing " + method); + } + + // Map between labels and the instruction that they label + final Map targets = new HashMap(); + + final LinkedList jumps = new LinkedList(); // Jump instructions + + Instruction next = null; + Instruction nextInst = null; + + final List code = method.code(); + + // Go backwards so we can eliminate redundant loads and stores + // in one pass. During the pass collect the locations of labels. + + CODE: for (int i = code.size() - 1; i >= 0; i--) { + final Object ce = code.get(i); + + if (ce instanceof Label) { + if (nextInst != null) { + targets.put(ce, nextInst); + } + + next = null; + + } else if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + Filter peep = null; + + // Have we seen a label that starts a block? (i.e. is a target) + boolean seenLabel = false; + + if (inst.isGoto()) { + // 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. + + final Label target = (Label) inst.operand(); + + for (int j = i + 1; j < code.size(); j++) { + final Object t = code.get(j); + + // Replace + // goto L + // L: inst + // with + // L: inst + // + if (t instanceof Label) { + if (((Label) t).startsBlock()) { + seenLabel = true; + } + + if (target.equals(t)) { + code.remove(i); + next = null; + nextInst = null; + continue CODE; + } + + continue; + } + + // Replace + // goto L + // this is unreachable + // M: inst (M is a different label from L!) + // with + // goto L + // M: inst + // + if (t instanceof Instruction) { + if (seenLabel) { + break; + } + + code.remove(j); + j--; + } + } + } + + if (inst.isGoto() || inst.isSwitch()) { + jumps.add(inst); + } + + // 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) { + peep = Peephole.filter(inst, next); + } + + if (peep != null) { + if (ClassEditor.DEBUG) { + if (peep.replace.length == 0) { + System.out.println("eliminate " + code.get(i) + "-" + + code.get(i + 1)); + + } else { + System.out.println("replace " + code.get(i) + "-" + + code.get(i + 1)); + System.out.println(" with"); + for (int j = 0; j < peep.replace.length; j++) { + System.out.println(" " + peep.replace[j]); + } + } + } + + // Remove old instructions + code.remove(i + 1); + code.remove(i); + + // Add new instructions resulting from peephole + // optimizations + for (int j = peep.replace.length - 1; j >= 0; j--) { + code.add(i, peep.replace[j]); + } + + if ((i < code.size()) + && (code.get(i) instanceof Instruction)) { + next = (Instruction) code.get(i); + + } else { + // No more instructions, or next thing is a label + next = null; + } + + } else { + // Filter didn't find any peephole optimizations, skip to + // next pair of instructions. + next = inst; + } + + nextInst = next; + } + } + + // Replace the target of jumps to gotos with the goto target. + // Replace gotos to unconditional jumps with the unconditional jump. + while (!jumps.isEmpty()) { + final Instruction inst = (Instruction) jumps.removeFirst(); + + Instruction target; + + if (inst.isGoto()) { + target = (Instruction) targets.get(inst.operand()); + + if (target != null) { + if (target.isGoto() + && !target.operand().equals(inst.operand())) { + + if (ClassEditor.DEBUG) { + System.out.println("replace " + inst); + } + + inst.setOperand(target.operand()); + + if (ClassEditor.DEBUG) { + System.out.println(" with " + inst); + } + + jumps.add(inst); + + } else if (target.isSwitch() || target.isReturn() + || target.isThrow()) { + + if (ClassEditor.DEBUG) { + System.out.println("replace " + inst); + } + + inst.setOpcodeClass(target.opcodeClass()); + inst.setOperand(target.operand()); + + if (ClassEditor.DEBUG) { + System.out.println(" with " + inst); + } + } + } + } + } + + // Remove unreachable code. + Peephole.removeUnreachable(method, code); + + if (ClassEditor.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 static void removeUnreachable(final MethodEditor method, + final List code) { + // Maps Labels to their instruction position + final Map labelPos = new HashMap(); + + // Collect all the ret targets. + Iterator iter = code.iterator(); + int i = 0; + + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (ce instanceof Label) { + labelPos.put(ce, new Integer(i)); + } + + i++; + } + + // Visit the blocks depth-first. + + // Stack of Labels that begin blocks that have been visited + final Set visited = new HashSet(); + + // Stack of Labels that begin blocks that have not been visited + final Stack stack = new Stack(); + + Label label; // Current label + + if (code.size() > 0) { + // Start with the label of the first block + label = (Label) code.get(0); + visited.add(label); + stack.push(label); + } + + final Iterator e = method.tryCatches().iterator(); + + while (e.hasNext()) { + // All exception handlers are considered to be live + final TryCatch tc = (TryCatch) e.next(); + visited.add(tc.handler()); + stack.push(tc.handler()); + } + + while (!stack.isEmpty()) { + label = (Label) stack.pop(); + + final Integer labelIndex = (Integer) labelPos.get(label); + Assert.isTrue(labelIndex != null, "Index of " + label + + " not found"); + + i = labelIndex.intValue(); + final ListIterator blockIter = code.listIterator(i + 1); + + while (blockIter.hasNext()) { + // 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. + + final Object ce = blockIter.next(); + i++; + + if (ce instanceof Instruction) { + final Instruction inst = (Instruction) ce; + + if (inst.isReturn() || inst.isThrow()) { + // We've reached the end of the block, but we don't know + // which block will be executed next. + break; + + } else if (inst.isConditionalJump() || inst.isJsr()) { + // 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 = (Label) inst.operand(); + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + // Fall through. + + } else if (inst.isGoto()) { + // Add next block to work list. + + label = (Label) inst.operand(); + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + break; + + } else if (inst.isRet()) { + // The ret targets were handled by the jsr. + break; + + } else if (inst.isSwitch()) { + // A switch. Add all possible targets of the switch to + // the worklist. + + final Switch sw = (Switch) inst.operand(); + + label = sw.defaultTarget(); + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + + final Label[] targets = sw.targets(); + + for (int j = 0; j < targets.length; j++) { + label = targets[j]; + + if (!visited.contains(label)) { + visited.add(label); + stack.push(label); + } + } + + break; + } + + } else if (ce instanceof Label) { + label = (Label) ce; + visited.add(label); + } + } + } + + boolean reachable = false; + + iter = code.iterator(); + + // Remove unreachable instructions + while (iter.hasNext()) { + final Object ce = iter.next(); + + if (ce instanceof Label) { + reachable = visited.contains(ce); + // Don't remove unreachable labels, only instructions. + + } else if (!reachable) { + if (ClassEditor.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; + + Filter() { + this.replace = new Instruction[0]; + } + + Filter(final Instruction replace) { + this.replace = new Instruction[] { replace }; + } + + Filter(final Instruction replace1, final 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 static Filter filter(final Instruction first, + final Instruction second) { + 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: + // Elminate swap-swap + if (first.opcodeClass() == Opcode.opcx_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.opcodeClass() == Opcode.opcx_dup) { + return new Filter(first); + } + break; + + // Eliminate push-pop. + case opcx_pop: + // Eliminate push-pop. + + switch (first.opcodeClass()) { + case opcx_ldc: + // Make sure things being popped off is not wide (we're + // dealing with a pop not a pop2). + Assert.isTrue(!(first.operand() instanceof Long) + && !(first.operand() instanceof Double), + "Cannot pop a 2-word operand"); + // Fall through. + + case opcx_iload: + case opcx_fload: + case opcx_aload: + case opcx_dup: + // Eliminate the load and the pop. + return new Filter(); + + case opcx_dup_x1: + // Replace dup_x1-pop with swap + // (As if this is really likely to happen ;) <-- Nate made a + // joke! + return new Filter(new Instruction(Opcode.opcx_swap)); + } + break; + + case opcx_pop2: + switch (first.opcodeClass()) { + case opcx_ldc: + Assert.isTrue((first.operand() instanceof Long) + || (first.operand() instanceof Double), + "Cannot pop2 a 1-word operand"); + // Fall through. + + case opcx_lload: + case opcx_dload: + case opcx_dup2: + // Eliminate push and pop + return new Filter(); + } + break; + + case opcx_istore: + // Eliminate load-store to same location. + + if (first.opcodeClass() == Opcode.opcx_iload) { + if (first.operand().equals(second.operand())) { + return new Filter(); + } + } + break; + + case opcx_fstore: + if (first.opcodeClass() == Opcode.opcx_fload) { + if (first.operand().equals(second.operand())) { + return new Filter(); + } + } + break; + + case opcx_astore: + if (first.opcodeClass() == Opcode.opcx_aload) { + if (first.operand().equals(second.operand())) { + return new Filter(); + } + } + break; + + case opcx_lstore: + if (first.opcodeClass() == Opcode.opcx_lload) { + if (first.operand().equals(second.operand())) { + return new Filter(); + } + } + break; + + case opcx_dstore: + if (first.opcodeClass() == Opcode.opcx_dload) { + if (first.operand().equals(second.operand())) { + return new Filter(); + } + } + break; + + case opcx_ireturn: + case opcx_freturn: + case opcx_areturn: + case opcx_lreturn: + case opcx_dreturn: + // Replace store-return with return. Remember that upon return + // all local variables revert to their pre-call values, so any + // stores are destroyed. + + switch (first.opcodeClass()) { + case opcx_istore: + case opcx_fstore: + case opcx_astore: + case opcx_lstore: + case opcx_dstore: + return new Filter(second); + } + break; + + case opcx_iadd: + // Replace ineg-iadd with isub + + if (first.opcodeClass() == Opcode.opcx_ineg) { + return new Filter(new Instruction(Opcode.opcx_isub)); + } + break; + + case opcx_isub: + // Replace ineg-isub with iadd + + if (first.opcodeClass() == Opcode.opcx_ineg) { + return new Filter(new Instruction(Opcode.opcx_iadd)); + } + break; + + case opcx_ladd: + // Replace lneg-ladd with lsub + + if (first.opcodeClass() == Opcode.opcx_lneg) { + return new Filter(new Instruction(Opcode.opcx_lsub)); + } + break; + + case opcx_lsub: + // Replace lneg-lsub with ladd + + if (first.opcodeClass() == Opcode.opcx_lneg) { + return new Filter(new Instruction(Opcode.opcx_ladd)); + } + break; + + case opcx_if_icmpeq: + // Replace ldc 0-if_icmpeq with ifeq + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_ifeq, + second.operand())); + } + } + } + break; + + case opcx_if_icmpne: + // Replace ldc 0-if_icmpne with ifne + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_ifne, + second.operand())); + } + } + } + break; + + case opcx_if_icmplt: + // Replace ldc 0-if_icmplt with iflt + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_iflt, + second.operand())); + } + } + } + break; + + case opcx_if_icmpge: + // Replace ldc 0-if_icmpge with ifge + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_ifge, + second.operand())); + } + } + } + break; + + case opcx_if_icmpgt: + // Replace ldc 0-if_icmpgt with ifgt + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_ifgt, + second.operand())); + } + } + } + break; + + case opcx_if_icmple: + // Replace ldc 0-if_icmple with ifle + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_ifle, + second.operand())); + } + } + } + break; + + case opcx_if_acmpeq: + // Replace ldc null-if_acmpeq with ifnull + + if (first.opcodeClass() == Opcode.opcx_ldc) { + if (first.operand() == null) { + return new Filter(new Instruction(Opcode.opcx_ifnull, + second.operand())); + } + } + break; + + case opcx_if_acmpne: + // Replace ldc null-if_acmpne with ifnonnull + + if (first.opcodeClass() == Opcode.opcx_ldc) { + if (first.operand() == null) { + return new Filter(new Instruction(Opcode.opcx_ifnonnull, + second.operand())); + } + } + break; + + case opcx_ifeq: + // Replace ldc 0-ifeq with goto and eliminate ldc !0-ifeq + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() == 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + + case opcx_ifne: + // Replace ldc !0-ifne with goto and eliminate ldc 0-ifne + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() != 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + + case opcx_iflt: + // Replace ldc <0-iflt with goto and eliminate ldc >=0-iflt + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() < 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + + case opcx_ifge: + // Replace ldc >=0-ifge with goto and eliminate ldc <0-ifge + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() >= 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + + case opcx_ifgt: + // Replace ldc >0-ifgt with goto and eliminate ldc <=0-ifgt + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() > 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + + case opcx_ifle: + // Replace ldc <=0-ifle with goto and eliminate ldc >0-ifle + + if (first.opcodeClass() == Opcode.opcx_ldc) { + final Object op = first.operand(); + if (op instanceof Integer) { + if (((Integer) op).intValue() <= 0) { + return new Filter(new Instruction(Opcode.opcx_goto, + second.operand())); + } else { + return new Filter(); + } + } + } + break; + } + + switch (second.opcodeClass()) { + // Replace store-store to same location with pop-store. + + case opcx_istore: + case opcx_fstore: + case opcx_astore: + case opcx_lstore: + case opcx_dstore: + switch (first.opcodeClass()) { + case opcx_istore: + case opcx_fstore: + case opcx_astore: + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_pop), first); + } + break; + case opcx_lstore: + case opcx_dstore: + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_pop2), first); + } + break; + } + break; + } + + switch (second.opcodeClass()) { + // Replace store-load with dup-store. + // Replace load-load with load-dup. + case opcx_iload: + if (first.opcodeClass() == Opcode.opcx_istore) { + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_dup), first); + } + } + if (first.opcodeClass() == Opcode.opcx_iload) { + if (first.operand().equals(second.operand())) { + return new Filter(first, new Instruction(Opcode.opcx_dup)); + } + } + break; + + case opcx_fload: + if (first.opcodeClass() == Opcode.opcx_fstore) { + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_dup), first); + } + } + if (first.opcodeClass() == Opcode.opcx_fload) { + if (first.operand().equals(second.operand())) { + return new Filter(first, new Instruction(Opcode.opcx_dup)); + } + } + break; + + case opcx_aload: + if (first.opcodeClass() == Opcode.opcx_astore) { + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_dup), first); + } + } + if (first.opcodeClass() == Opcode.opcx_aload) { + if (first.operand().equals(second.operand())) { + return new Filter(first, new Instruction(Opcode.opcx_dup)); + } + } + break; + + case opcx_lload: + if (first.opcodeClass() == Opcode.opcx_lstore) { + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_dup2), first); + } + } + if (first.opcodeClass() == Opcode.opcx_lload) { + if (first.operand().equals(second.operand())) { + return new Filter(first, new Instruction(Opcode.opcx_dup2)); + } + } + break; + + case opcx_dload: + if (first.opcodeClass() == Opcode.opcx_dstore) { + if (first.operand().equals(second.operand())) { + return new Filter(new Instruction(Opcode.opcx_dup2), first); + } + } + if (first.opcodeClass() == Opcode.opcx_dload) { + if (first.operand().equals(second.operand())) { + return new Filter(first, new Instruction(Opcode.opcx_dup2)); + } + } + break; + } + + return null; + } +} diff --git a/src/EDU/purdue/cs/bloat/trans/PersistentCheckElimination.java b/src/EDU/purdue/cs/bloat/trans/PersistentCheckElimination.java new file mode 100644 index 0000000..f156eed --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/PersistentCheckElimination.java @@ -0,0 +1,232 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Attempts to remove residency checks an update checks from a control flow + * graph. + */ +public class PersistentCheckElimination { + public static boolean DEBUG = false; + + private static final int RC = 0; + + private static final int AUPDATE = 1; + + private static final int SUPDATE = 2; + + private static final int SIZE = 3; // Number of persistent opcodes + + private EditorContext context; + + /** + * Examines each residency check (RCExpr) and update check (UCExpr) + * and determines whether or it is redundent. If a residency check checks + * something that we know is resident (i.e. the this pointer or the result + * of an object creation), then the check is redundent. Once an update check + * has been performed on a value, all subsequent checks are redundent. + * Redundent checks are removed from the control flow graph. + * + * @see RCExpr + * @see UCExpr + */ + public void transform(final FlowGraph cfg) { + context = cfg.method().declaringClass().context(); + + final BitSet[] seen = new BitSet[PersistentCheckElimination.SIZE]; + + for (int i = 0; i < PersistentCheckElimination.SIZE; i++) { + seen[i] = new BitSet(); + } + + search(cfg, cfg.source(), seen); + } + + /** + * Recursively searches the tree for residency statements that can be + * removed. The value numbers of expressions that create new objects are + * noted. The value number of the "this" pointer is also noted. If a + * residency check (RCExpr) is performed on any of these expressions, it is + * redundent and can be removed. + * + *

      + * + * When an update check (UCExpr) is encountered each of its children is + * visited. The value numbers of expressions that are checked (i.e. wrapped + * in UCExpr) are noted. If an UCExpr is encountered that checks an + * expression with one of these value numbers, the check is redundent and + * can be removed. + * + * @param seen + * An array containing a BitSet for each type of + * residency-related instruction (RC, AUPDATE, SUPDATE). Each bit + * in the BitSet corresponds to an expression involving residency + * being visited. That is, its value number is "seen". + */ + private void search(final FlowGraph cfg, final Block block, + final BitSet[] seen) { + // Accumulate the information stored in seen... + // Save a copy of the contens of seen + final BitSet[] save = new BitSet[PersistentCheckElimination.SIZE]; + + for (int i = 0; i < PersistentCheckElimination.SIZE; i++) { + save[i] = new BitSet(seen[i].size()); + save[i].or(seen[i]); + } + + // Visit each expression in the tree. + block.visit(new TreeVisitor() { + // When we reach an expression that creates a new object + // (NewArrayExpr, NewMultiArrayExpr, or NewExpr), set the bit in + // the RC bit vector corresponding to the expression's value + // number. + public void visitNewArrayExpr(final NewArrayExpr expr) { + expr.visitChildren(this); + + final int v = expr.valueNumber(); + Assert.isTrue(v != -1); + seen[PersistentCheckElimination.RC].set(v); + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + expr.visitChildren(this); + + final int v = expr.valueNumber(); + Assert.isTrue(v != -1); + seen[PersistentCheckElimination.RC].set(v); + } + + public void visitNewExpr(final NewExpr expr) { + expr.visitChildren(this); + + final int v = expr.valueNumber(); + Assert.isTrue(v != -1); + seen[PersistentCheckElimination.RC].set(v); + } + + // Find the value number of the LocalExpr corresponding to the + // "this" variable. Set the bit in the RC bit vector + // corresponding to the "this" pointer's value number. + public void visitInitStmt(final InitStmt stmt) { + stmt.visitChildren(this); + + final MethodEditor method = stmt.block().graph().method(); + + if (!method.isStatic()) { + Assert.isTrue(stmt.targets().length > 0); + final int v = stmt.targets()[0].valueNumber(); + Assert.isTrue(v != -1); + seen[PersistentCheckElimination.RC].set(v); + } + } + + // If the expression being checked by the RCExpr is either the + // result of a "new" expression or it is the "this" pointer, we + // know that it is resident and thus does not need to be + // checked. All occurrences of the RCExpr are replaced with the + // expression being checked. All of this is contingent on the + // fact that the check does not have an ALIAS side effect. + public void visitRCExpr(final RCExpr expr) { + expr.visitChildren(this); + + final int v = expr.expr().valueNumber(); + Assert.isTrue(v != -1); + + final SideEffectChecker sideEffects = new SideEffectChecker( + context); + expr.expr().visit(sideEffects); + final int flag = sideEffects.sideEffects(); + + if (seen[PersistentCheckElimination.RC].get(v) + && ((flag & SideEffectChecker.ALIAS) == 0)) { + // rc(x) --> x + expr.expr().setParent(null); + expr.replaceWith(expr.expr(), false); + expr.cleanupOnly(); + } + + seen[PersistentCheckElimination.RC].set(v); + } + + // If an object has already been updated, it does not need to be + // updated again. Note that the children of the UCExpr are + // visited first. There is a seen bit vector for both AUPDATE + // and SUPDATE UCExpr. If the value number of the expression + // being checked has already been encountered in a UCExpr, then + // the UCExpr is redundent and can be replaced with the + // expression being checked provided that it has no ALIAS side + // effects. + public void visitUCExpr(final UCExpr expr) { + expr.visitChildren(this); + + final int v = expr.expr().valueNumber(); + Assert.isTrue(v != -1); + + final SideEffectChecker sideEffects = new SideEffectChecker( + context); + expr.expr().visit(sideEffects); + final int flag = sideEffects.sideEffects(); + + if (expr.kind() == UCExpr.POINTER) { + if (seen[PersistentCheckElimination.AUPDATE].get(v) + && ((flag & SideEffectChecker.ALIAS) == 0)) { + // aupdate(x) --> x + expr.expr().setParent(null); + expr.replaceWith(expr.expr(), false); + expr.cleanupOnly(); + } + + seen[PersistentCheckElimination.AUPDATE].set(v); + + } else { + if (seen[PersistentCheckElimination.SUPDATE].get(v) + && ((flag & SideEffectChecker.ALIAS) == 0)) { + // supdate(x) --> x + expr.expr().setParent(null); + expr.replaceWith(expr.expr(), false); + expr.cleanupOnly(); + } + + seen[PersistentCheckElimination.SUPDATE].set(v); + } + } + }); + + // Visit the blocks in the dominator tree in pre-order + final Iterator children = cfg.domChildren(block).iterator(); + + while (children.hasNext()) { + final Block child = (Block) children.next(); + search(cfg, child, seen); + } + + for (int i = 0; i < PersistentCheckElimination.SIZE; i++) { + seen[i] = save[i]; + } + } +} 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..f7b6a16 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/SSAPRE.java @@ -0,0 +1,3672 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tbaa.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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 { + 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 HashMap phiRelated; + + /** + * Constructor. + * + * @param cfg + * Control flow graph on which to perform SSA-based PRE. + * @param context The EditorContext containing all the classes that BLOAT + * knows about. + */ + public SSAPRE(final FlowGraph cfg, final EditorContext context) { + this.cfg = cfg; + this.context = context; + } + + /** + * Performs SSA-based partial redundency elimination (PRE) on a control flow + * graph. + */ + public void transform() { + 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 HashMap(); + + // Compile the worklist of expressions on which to perform SSAPRE. + collectOccurrences(); + + // Do the transformation for each expression. + while (!worklist.isEmpty()) { + final 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(final ExprInfo exprInfo) { + if (SSAPRE.DEBUG) { + System.out.println("PRE for " + exprInfo.prototype() + + " -------------------------"); + } + + if (exprInfo.numUses() == 0) { + if (SSAPRE.DEBUG) { + System.out.println("Skipping...all occurrences are " + + "as targets. -------------------------"); + } + + exprInfo.cleanup(); + return; + } + + if (SSAPRE.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 (SSAPRE.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 (SSAPRE.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 (SSAPRE.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 (SSAPRE.DEBUG) { + System.out.println("Finalize for " + exprInfo.prototype() + + " -------------------------"); + } + + finalize(exprInfo); + + if (SSAPRE.DEBUG) { + System.out.println("Code motion for " + exprInfo.prototype() + + " -------------------------"); + } + + final Type type = exprInfo.prototype().type(); + final LocalVariable v = cfg.method().newLocal(type); + final VarExpr tmp = new LocalExpr(v.index(), type); + + final SSAConstructionInfo consInfo = new SSAConstructionInfo(cfg, tmp); + codeMotion(exprInfo, tmp, consInfo); + + if (SSAPRE.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. + + 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 (SSAPRE.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(final 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(final PhiStmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + stmt.visitChildren(this); + + final 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()) { + final Expr operand = (Expr) iter.next(); + + if (operand instanceof VarExpr) { + if (operand.def() != null) { + phiRelatedUnion(operand.def(), stmt.target()); + } + } + } + } + + public void visitConstantExpr(final ConstantExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.setKey(count.value++); + } + + public void visitVarExpr(final VarExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.setKey(count.value++); + } + + public void visitCatchExpr(final 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(final MonitorStmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + if (!SSAPRE.NO_THREAD) { + stmt.visitChildren(this); + stmt.setKey(count.value++); + worklist.addKill(stmt.block(), new MemRefKill(stmt.key())); + } + } + + public void visitCallExpr(final CallExpr expr) { + if (maxValue.value < expr.valueNumber()) { + maxValue.value = expr.valueNumber(); + } + + expr.visitChildren(this); + expr.setKey(count.value++); + worklist.addKill(expr.block(), new MemRefKill(expr.key())); + } + + public void visitMemRefExpr(final 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()) { + worklist.addKill(expr.block(), new MemRefKill(expr, expr + .key())); + } + + if (firstOrder) { + worklist.addReal(expr); + } + } + + public void visitStmt(final Stmt stmt) { + if (maxValue.value < stmt.valueNumber()) { + maxValue.value = stmt.valueNumber(); + } + + stmt.visitChildren(this); + } + + public void visitExpr(final 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() { + final Set beginTry = new HashSet(); + + final Iterator blocks = cfg.catchBlocks().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Handler handler = (Handler) cfg.handlersMap().get(block); + + if (handler != null) { + final HashSet p = new HashSet(); + + final Iterator prots = handler.protectedBlocks().iterator(); + + while (prots.hasNext()) { + final 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. + final Iterator preds = p.iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + beginTry.addAll(cfg.succs(pred)); + } + } + } + + return beginTry; + } + + private void enqueueParents(final SSAConstructionInfo consInfo) { + final Set seen = new HashSet(); + + final Iterator iter = cfg.nodes().iterator(); + + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + + final Iterator e = consInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + final 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)) { + final Expr expr = (Expr) p; + + seen.add(p); + + if (isFirstOrder(expr)) { + worklist.addReal(expr); + } + } + } + } + } + + private void setValueNumbers(final 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; + + final List postOrder = cfg.postOrder(); + final ListIterator iter = postOrder.listIterator(postOrder.size()); + + while (iter.hasPrevious()) { + final Block block = (Block) iter.previous(); + + final PhiStmt phi = consInfo.phiAtBlock(block); + + if (phi != null) { + if (phi.target().valueNumber() == -1) { + phi.target().setValueNumber(nextValueNumber++); + changed = true; + } + + final Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + final VarExpr operand = (VarExpr) operands.next(); + + if (operand == null) { + continue; + } + + final 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; + } + } + } + + final Iterator e = consInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + final VarExpr real = (VarExpr) e.next(); + + if (real.isDef()) { + Assert.isTrue(real.parent() instanceof StoreExpr); + + final StoreExpr store = (StoreExpr) real.parent(); + final 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 { + final 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; + } + } + } + } + } + + final Iterator iter = cfg.nodes().iterator(); + + while (iter.hasNext()) { + final Block block = (Block) iter.next(); + + final PhiStmt phi = consInfo.phiAtBlock(block); + + if (phi != null) { + + final Iterator operands = phi.operands().iterator(); + + while (operands.hasNext()) { + final 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 + final ArrayList w = new ArrayList(cfg.size()); + + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final 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 HashSet(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 HashSet(); + + // 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()) { + final Block block = (Block) blocks.next(); + + final Iterator e = exprInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + final Expr real = (Expr) e.next(); + + real.visit(new TreeVisitor() { + public void visitVarExpr(final VarExpr var) { + final Expr def = var.def(); + + if (def == null) { + return; + } + + final 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()) { + final PhiStmt phi = (PhiStmt) worklist.remove(worklist.size() - 1); + df.add(phi.block()); + + final Iterator iter = phi.operands().iterator(); + + while (iter.hasNext()) { + final Expr expr = (Expr) iter.next(); + + if (expr instanceof VarExpr) { + final VarExpr var = (VarExpr) expr; + + final Expr def = var.def(); + + if (def == null) { + continue; + } + + final 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. + final Iterator iter = df.iterator(); + + while (iter.hasNext()) { + final 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(final 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. + + final 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. + + final HashSet seen = new HashSet(); + + final LinkedList leavesWorklist = new LinkedList(); + + final 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(final StoreExpr expr) { + // This should have been checked before adding + // the real to the worklist. + throw new RuntimeException(); + } + + public void visitConstantExpr(final ConstantExpr expr) { + leaves.add(expr); + } + + public void visitVarExpr(final VarExpr expr) { + leaves.add(expr.def()); + } + + public void visitExpr(final 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. + final 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) { + final 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(final Expr expr) { + throw new RuntimeException(); + } + + public void visitStoreExpr(final StoreExpr expr) { + expr.target().visit(this); + } + + public void visitConstantExpr(final ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(final VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(final 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) { + final 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) { + final ArrayList newLeaves = new ArrayList(leaves.size()); + final Phi opPhi = (Phi) operand; + + final 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) { + final 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. + final Pair pair = new Pair(phi, opPhi); + + if (!seen.contains(pair)) { + seen.add(pair); + opPhi.setLeaves(newLeaves); + leavesWorklist.add(opPhi); + } + } + } + } + + // Remove the dead phis. + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if ((phi != null) && !phi.live()) { + if (SSAPRE.DEBUG) { + System.out.println(" dead Phi at " + block); + } + + exprInfo.removePhi(block); + } + } + } + + class Pair { + Object a; + + Object b; + + Pair(final Object a, final Object b) { + this.a = a; + this.b = b; + } + + public boolean equals(final 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(final Block block, final ExprInfo exprInfo, Expr top, + Def topdef, final List renameWorklist) { + if (SSAPRE.DEBUG) { + System.out.println(" renaming in " + block); + } + + final 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 (!SSAPRE.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 (SSAPRE.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. + final Iterator e = exprInfo.occurrencesAtBlock(block).iterator(); + + while (e.hasNext()) { + final Object obj = e.next(); + + if (obj instanceof Kill) { + if (topdef != null) { + final Kill kill = (Kill) obj; + + if (SSAPRE.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) { + final MemRefExpr k = (MemRefExpr) kill.expr; + final 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 (SSAPRE.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). + final 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(final StoreExpr expr) { + hasStore.value = true; + } + + public void visitExpr(final Expr expr) { + if (!hasStore.value) { + expr.visitChildren(this); + } + } + }); + } + + boolean matches = true; + + if (hasStore.value) { + matches = false; + + if (SSAPRE.DEBUG) { + System.out.println("real has store"); + } + } + + if (matches && (topdef == null)) { + matches = false; + + if (SSAPRE.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 (SSAPRE.DEBUG) { + System.out.println("uses var defined after topdef"); + } + } + } + + if (matches && (top != null)) { + if (!matches(top, real)) { + matches = false; + + if (SSAPRE.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 + final 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 (SSAPRE.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; + } + + final 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()) { + final 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); + } + } + + final Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + if (succPhi != null) { + succPhi.setOperandAt(block, topdef); + + if (top != null) { + succPhi.setHasRealUse(block, true); + } + } + } + + final Iterator children = cfg.domChildren(block).iterator(); + + // Visit each child in the dominator tree. + while (children.hasNext()) { + final 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(final Expr expr) { + if (match.value) { + expr.visitChildren(this); + } + } + + public void visitStoreExpr(final StoreExpr expr) { + // A store means a new SSA number, so they won't match + match.value = false; + } + + public void visitVarExpr(final 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. + + final VarExpr def = (VarExpr) expr.def(); + + if (def == null) { + match.value = false; + return; + } + + final Block block = phi.block(); + + final 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(final StoreExpr expr) { + expr.target().visit(this); + } + + public void visitConstantExpr(final ConstantExpr expr) { + leaves.add(expr); + } + + public void visitVarExpr(final VarExpr expr) { + leaves.add(expr); + } + }); + + final Bool match = new Bool(); + match.value = true; + + expr2.visit(new TreeVisitor() { + public void visitExpr(final Expr expr) { + if (match.value) { + expr.visitChildren(this); + } + } + + public void visitStoreExpr(final StoreExpr expr) { + if (match.value) { + expr.target().visit(this); + } + } + + public void visitConstantExpr(final ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(final VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(final Expr expr) { + if (leaves.isEmpty()) { + match.value = false; + return; + } + + final Expr leaf = (Expr) leaves.removeFirst(); + + if ((leaf == null) + || (expr.valueNumber() != leaf.valueNumber())) { + match.value = false; + } + } + }); + + return match.value; + } + + private Expr buildPhiOperand(final ExprInfo exprInfo, final Phi phi, + final Block pred) { + final Iterator leaves = phi.leaves().iterator(); + + final Expr expr = (Expr) exprInfo.prototype().clone(); + + expr.visit(new TreeVisitor() { + public void visitExpr(final StoreExpr expr) { + throw new RuntimeException(); + } + + public void visitConstantExpr(final ConstantExpr expr) { + visitLeaf(expr); + } + + public void visitVarExpr(final VarExpr expr) { + visitLeaf(expr); + } + + public void visitLeaf(final Expr expr) { + if (leaves.hasNext()) { + Expr leaf = (Expr) leaves.next(); + + if (leaf instanceof VarExpr) { + Assert.isTrue(((VarExpr) leaf).isDef()); + + if (leaf.parent() instanceof PhiJoinStmt) { + final 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); + + final 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(final ExprInfo exprInfo) { + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if ((phi == null) || phi.downSafe()) { + continue; + } + + final Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + resetDownSafe(phi, pred); + } + } + } + + private void resetDownSafe(final Phi phi, final Block block) { + if (phi.hasRealUse(block)) { + return; + } + + final Def def = phi.operandAt(block); + + if (def instanceof Phi) { + final Phi phidef = (Phi) def; + + if (phidef.downSafe()) { + phidef.setDownSafe(false); + + if (SSAPRE.DEBUG) { + System.out.println(" def = Phi in " + + phidef.block()); + System.out.println(" def made not down safe"); + } + + final Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + final 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(final 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(final ExprInfo exprInfo) { + final Iterator blocks = cfg.nodes().iterator(); + + // Go through every PHI-statement of the exprInfo. + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (!phi.canBeAvail()) { + continue; + } + + if (phi.downSafe()) { + continue; + } + + final 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()) { + final Block pred = (Block) e.next(); + + final 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(final ExprInfo exprInfo, final Phi phi) { + phi.setCanBeAvail(false); + + final 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()) { + final Block block = (Block) blocks.next(); + + final Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + final Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + + final 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(final ExprInfo exprInfo) { + Iterator blocks = cfg.nodes().iterator(); + + // Initialize later to can be available... + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final 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()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (!phi.later()) { + continue; + } + + final 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()) { + final Block pred = (Block) e.next(); + final 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(final ExprInfo exprInfo, final Phi phi) { + phi.setLater(false); + + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + final Iterator e = cfg.preds(block).iterator(); + + // For PHI-statement that has the "h" defined by phi as an + // operand... + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + final 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(final 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, final Block block, + Def top) { + if (SSAPRE.DEBUG) { + System.out.println(" finalizing " + block); + } + + // Get the (only) PHI-statement at the current block. If wba = 1 for + // the PHI-statement, + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + if (phi.willBeAvail()) { + exprInfo.setAvailDef(phi, phi); + top = phi; + } else { + top = null; + } + } + + final Iterator reals = exprInfo.realsAtBlock(block).iterator(); + + // Iterate over all of the real occurrences in the block. + + while (reals.hasNext()) { + final Expr real = (Expr) reals.next(); + + if (SSAPRE.DEBUG) { + System.out.println(" -----------"); + } + + // Get defining "h" occurrence for the expression + final Def def = exprInfo.def(real); + Assert.isTrue(def != null, real + " is undefined"); + + // Get Avail_def[i][x] + final 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); + } + } + + final Iterator succs = cfg.succs(block).iterator(); + + // Iterate over each successor block in the CFG... + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final 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 { + final Def operand = succPhi.operandAt(block); + + Assert.isTrue(operand != null); + + final Def availDef = exprInfo.availDef(operand); + + if (availDef instanceof RealDef) { + exprInfo.setSave(((RealDef) availDef).expr, true); + } + } + } + } + } + + final Iterator children = cfg.domChildren(block).iterator(); + + while (children.hasNext()) { + final Block child = (Block) children.next(); + finalizeVisit(exprInfo, child, top); + } + } + + private void codeMotion(final ExprInfo exprInfo, final VarExpr tmp, + final SSAConstructionInfo consInfo) { + final List[] targets = new List[cfg.size()]; + + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + final Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + + if (!phi.saveOperand(pred)) { + continue; + } + + final Expr operand = buildPhiOperand(exprInfo, phi, pred); + Assert.isTrue(operand != null); + + final VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(operand.valueNumber()); + + final StoreExpr store = new StoreExpr(t, operand, t.type()); + store.setValueNumber(operand.valueNumber()); + pred.tree().addStmtBeforeJump(new ExprStmt(store)); + + if (SSAPRE.DEBUG) { + System.out.println("Created new store: " + store); + } + + // Save the target for later since we need to add + // it to consInfo last. + final int predIndex = cfg.preOrderIndex(pred); + + if (targets[predIndex] == null) { + targets[predIndex] = new ArrayList(); + } + + targets[predIndex].add(t); + + if (SSAPRE.DEBUG) { + System.out.println("insert at end of " + pred + ": " + + store); + } + } + } + + final Iterator e = exprInfo.realsAtBlock(block).iterator(); + + while (e.hasNext()) { + final 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()) { + final Block block = (Block) blocks.next(); + final int blockIndex = cfg.preOrderIndex(block); + + if (targets[blockIndex] != null) { + final Iterator iter = targets[blockIndex].iterator(); + + while (iter.hasNext()) { + final VarExpr t = (VarExpr) iter.next(); + consInfo.addReal(t); + } + } + } + } + + private void save(final ExprInfo exprInfo, final VarExpr tmp, + final Expr real, final SSAConstructionInfo consInfo) { + if (SSAPRE.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. + + final Node parent = real.parent(); + final VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(real.valueNumber()); + + final StoreExpr store = new StoreExpr(t, real, real.type()); + store.setValueNumber(real.valueNumber()); + parent.visit(new ReplaceVisitor(real, store)); + + consInfo.addReal(t); + + if (SSAPRE.DEBUG) { + System.out.println("END SAVE--------------------------------"); + } + } + + private void reload(final ExprInfo exprInfo, final VarExpr tmp, + final Expr real, final SSAConstructionInfo consInfo) { + if (SSAPRE.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 (SSAPRE.DEBUG) { + System.out.println("reload t " + t + " in " + t.parent()); + } + + if (SSAPRE.DEBUG) { + System.out.println("END RELOAD--------------------------------"); + } + } + + private void saveTarget(final ExprInfo exprInfo, final VarExpr tmp, + final Expr real, final SSAConstructionInfo consInfo) { + if (SSAPRE.DEBUG) { + System.out.println("SAVE TARGET: " + real + " to " + tmp + + "--------------------------------"); + } + + Assert.isTrue(real instanceof MemRefExpr); + + // Replace + // a.b := c + // with: + // a.b := (t := c); + + final VarExpr t = (VarExpr) tmp.clone(); + t.setValueNumber(real.valueNumber()); + + final StoreExpr store = (StoreExpr) real.parent(); + final Expr rhs = store.expr(); + + final StoreExpr rhsStore = new StoreExpr(t, rhs, rhs.type()); + rhsStore.setValueNumber(real.valueNumber()); + store.visit(new ReplaceVisitor(rhs, rhsStore)); + + consInfo.addReal(t); + + if (SSAPRE.DEBUG) { + System.out.println("save target " + store); + } + + if (SSAPRE.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(final Expr expr) { + final 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(final 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(final Expr expr) { + if (expr instanceof StoreExpr) { + return ((StoreExpr) expr).target() instanceof LocalExpr; + } + + return (expr instanceof LocalExpr) + || (expr instanceof ConstantExpr); + } + + public void visitCheckExpr(final 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(final 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(final 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(final ArrayRefExpr expr) { + if (SSAPRE.NO_ACCESS_PATHS) { + return; + } + + if (isLeaf(expr.array()) && isLeaf(expr.index())) { + firstOrder = true; + } + } + + public void visitCastExpr(final 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(final FieldExpr expr) { + if (SSAPRE.NO_ACCESS_PATHS) { + return; + } + + if (isLeaf(expr.object())) { + try { + final FieldEditor e = context.editField(expr.field()); + + if (!e.isVolatile()) { + firstOrder = true; + } + + context.release(e.fieldInfo()); + + } catch (final NoSuchFieldException e) { + // A field wasn't found. Silently assume it's volatile. + firstOrder = false; + } + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + if (isLeaf(expr.expr())) { + firstOrder = true; + } + } + + public void visitNegExpr(final NegExpr expr) { + if (isLeaf(expr.expr())) { + firstOrder = true; + } + } + + public void visitShiftExpr(final 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(final StaticFieldExpr expr) { + if (SSAPRE.NO_ACCESS_PATHS) { + return; + } + + try { + final FieldEditor e = context.editField(expr.field()); + + if (!e.isVolatile()) { + firstOrder = true; + } + + context.release(e.fieldInfo()); + + } catch (final NoSuchFieldException e) { + // 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(final 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(final ExprInfo exprInfo, final Block block) { + this.block = block; + + final 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. + */ + public void setLeaves(final List leaves) { + if (SSAPRE.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); + + final Iterator iter = leaves.iterator(); + + while (iter.hasNext()) { + final 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() { + final LinkedList v = new LinkedList(); + + final Iterator preds = cfg.preds(block).iterator(); + + while (preds.hasNext()) { + final 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(final Block block, final Def def) { + final int blockIndex = cfg.preOrderIndex(block); + operands[blockIndex] = def; + + if (SSAPRE.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(final Block block) { + final int blockIndex = cfg.preOrderIndex(block); + return operands[blockIndex]; + } + + /** + * Sets the "has real use" flag. + */ + public void setHasRealUse(final Block block, final boolean flag) { + final int blockIndex = cfg.preOrderIndex(block); + + hasRealUse[blockIndex] = flag; + + if (SSAPRE.DEBUG) { + System.out.println(this); + } + } + + public boolean hasRealUse(final Block block) { + final int blockIndex = cfg.preOrderIndex(block); + return hasRealUse[blockIndex]; + } + + /** + * + */ + public void setSaveOperand(final Block block, final boolean flag) { + final int blockIndex = cfg.preOrderIndex(block); + saveOperand[blockIndex] = flag; + + if (SSAPRE.DEBUG) { + System.out.println(this); + } + } + + public boolean saveOperand(final Block block) { + final 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(final Block block) { + final int blockIndex = cfg.preOrderIndex(block); + + final Def def = operands[blockIndex]; + + if (def == null) { + return true; + } + + if (!hasRealUse[blockIndex]) { + if (def instanceof Phi) { + final 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(final boolean flag) { + canBeAvail = flag; + + if (SSAPRE.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(final boolean flag) { + later = flag; + + if (SSAPRE.DEBUG) { + System.out.println(this); + } + } + + /** + * Returns the "later" flag. + * + * @see #setLater + */ + public boolean later() { + return later; + } + + public void setLive(final 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(final boolean flag) { + downSafe = flag; + + if (SSAPRE.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) { + final Iterator e = cfg.preds(block).iterator(); + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + final int predIndex = cfg.preOrderIndex(pred); + + s += pred.label() + "="; + + final 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(final Expr expr, final 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(final StoreExpr expr) { + expr.target().setDef(null); + expr.target().setParent(null); + expr.replaceWith(expr.target(), false); + expr.cleanupOnly(); + expr.expr().cleanup(); + } + + public void visitVarExpr(final VarExpr expr) { + expr.setDef(null); + } + + public void visitConstantExpr(final ConstantExpr expr) { + } + + // The prototype expression should only + // contain StoreExpr, VarExpr, or + // ConstantExpr... + public void visitExpr(final 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 HashMap(); + availDefs = new HashMap(); + saves = new HashMap(); + reloads = new HashMap(); + + if (prototype instanceof MemRefExpr) { + // Traverse the tree and determine whether expr accesses a final + // field. + final 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(final Expr expr, final boolean flag) { + if (SSAPRE.DEBUG) { + System.out.println(" setting reload for " + expr + + " to " + flag); + } + + reloads.put(expr, new Boolean(flag)); + } + + public boolean reload(final Expr expr) { + final Boolean flag = (Boolean) reloads.get(expr); + return (flag != null) && flag.booleanValue(); + } + + // Save is used in finalize + public void setSave(final Expr expr, final boolean flag) { + if (SSAPRE.DEBUG) { + System.out.println(" setting save for " + expr + " to " + + flag); + } + + saves.put(expr, new Boolean(flag)); + } + + public boolean save(final Expr expr) { + final Boolean flag = (Boolean) saves.get(expr); + return (flag != null) && flag.booleanValue(); + } + + // AvailDef is used in finalize + public void setAvailDef(final Def def, final Def availDef) { + if (SSAPRE.DEBUG) { + System.out.println(" setting avail def for " + def + + " to " + availDef); + } + + availDefs.put(def, availDef); + } + + public Def availDef(final Def def) { + final Def availDef = (Def) availDefs.get(def); + + if (SSAPRE.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(final Expr expr, final Def def) { + if (SSAPRE.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(final Expr expr) { + final Def def = (Def) defs.get(expr); + + if (SSAPRE.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(final Expr real) { + if (!real.isDef()) { + numUses++; + } + + final 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) { + final int blockIndex = cfg.preOrderIndex(block); + + if (phis[blockIndex] == null) { + if (SSAPRE.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(final Block block) { + final 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(final Block block) { + final 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(final Block block) { + final int blockIndex = cfg.preOrderIndex(block); + + final 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(final Block block) { + if (isFinal && !hasSideEffects) { + return realsAtBlock(block); + } + + final 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(final Object obj) { + if (obj instanceof Kill) { + return a.contains(obj); + } else if (obj instanceof Expr) { + return r.contains(obj); + } + + return false; + } + + public Object get(final 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) { + final Object t = rnext; + + if (riter.hasNext()) { + rnext = (Expr) riter.next(); + } else { + rnext = null; + } + + return t; + + } else { + final 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(final List list) { + Collections.sort(list, new Comparator() { + public int compare(final Object a, final Object b) { + if (a == b) { + return 0; + } + + final int ka = ((Expr) a).key(); + final 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(final List list) { + Collections.sort(list, new Comparator() { + public int compare(final Object a, final Object b) { + if (a == b) { + return 0; + } + + final int ka = ((Kill) a).key(); + final 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(final Block block) { + phi = exprPhiAtBlock(block); + super.visitBlock(block); + } + + public void visitLabelStmt(final LabelStmt stmt) { + super.visitLabelStmt(stmt); + + if (stmt.label().startsBlock()) { + 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(final Expr expr) { + if (isFinal) { + expr.visitChildren(this); + } + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + isFinal = false; + } + + public void visitFieldExpr(final FieldExpr expr) { + final MemberRef field = expr.field(); + + try { + final FieldEditor e = context.editField(field); + if (!e.isFinal()) { + isFinal = false; + } + context.release(e.fieldInfo()); + + } catch (final NoSuchFieldException e) { + // A field wasn't found. Silently assume it's not final. + isFinal = false; + } + + if (isFinal) { + expr.visitChildren(this); + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + final MemberRef field = expr.field(); + + try { + final FieldEditor e = context.editField(field); + if (!e.isFinal()) { + isFinal = false; + } + context.release(e.fieldInfo()); + + } catch (final NoSuchFieldException e) { + // 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. + */ + class ExprWorklist { + Map exprInfos; // A mapping between ExprKey and ExprInfo + + LinkedList exprs; // All the ExprInfos we know about + + public ExprWorklist() { + exprInfos = new HashMap(); + exprs = new LinkedList(); + } + + public boolean isEmpty() { + return exprs.isEmpty(); + } + + public ExprInfo removeFirst() { + final 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(final Expr real) { + if (SSAPRE.DEBUG) { + System.out.println(" add to worklist=" + real); + } + + final 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 (SSAPRE.DEBUG) { + System.out.println(" add info"); + } + } + + exprInfo.addReal(real); + } + + /** + * Adds a Kill expression to the worklist at a given block. + */ + public void addKill(final Block block, final Kill kill) { + if (SSAPRE.DEBUG) { + System.out.println(" add alias to worklist=" + kill.expr + + " " + kill); + } + + final 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(final Expr expr, final int key) { + this.expr = expr; + this.key = key; + } + + public Kill(final int key) { + this(null, key); + } + + public int key() { + return 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. + */ + class ExceptionKill extends Kill { + public ExceptionKill(final Expr expr, final int key) { + super(expr, key); + } + + public ExceptionKill(final 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. + */ + class MemRefKill extends Kill { + public MemRefKill(final Expr expr, final int key) { + super(expr, key); + } + + public MemRefKill(final int key) { + super(key); + } + } + + /** + * Represents an expression and a hash code for that expression. + */ + class ExprKey { + Expr expr; + + int hash; + + public ExprKey(final 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(final StoreExpr expr) { + // Ignore the RHS. + children.add(expr.target()); + } + + public void visitExpr(final Expr expr) { + children.add(expr); + } + }); + + return children; + } + + public boolean equals(final Object obj) { + if (obj instanceof ExprKey) { + final ExprKey other = (ExprKey) obj; + + if (!expr.type().equals(other.expr.type())) { + return false; + } + + if (!NodeComparator.equals(expr, other.expr)) { + return false; + } + + final List children = listChildren(expr); + final List otherChildren = listChildren(other.expr); + + if (children.size() != otherChildren.size()) { + return false; + } + + final Iterator iter1 = children.iterator(); + final Iterator iter2 = otherChildren.iterator(); + + while (iter1.hasNext() && iter2.hasNext()) { + final Expr child1 = (Expr) iter1.next(); + final 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) { + final ArrayList stack = new ArrayList(); + + while (a != null) { + Object p = phiRelated.get(a); + + if ((p == a) || (p == null)) { + // Path compression. + final 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(final Expr a, final Expr b) { + final Expr p = phiRelatedFind(a); + final Expr q = phiRelatedFind(b); + if (p != q) { + phiRelated.put(p, q); + } + } +} 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..03f0481 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/SideEffectChecker.java @@ -0,0 +1,269 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * SideEffectChecker traverses a tree and determines is 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(final EditorContext context) { + this.context = context; + } + + public int sideEffects() { + return sideEffects; + } + + public boolean hasSideEffects() { + return sideEffects != 0; + } + + public void reset() { + sideEffects = 0; + } + + public void visitStoreExpr(final StoreExpr expr) { + sideEffects |= SideEffectChecker.STORE; + expr.visitChildren(this); + } + + public void visitLocalExpr(final LocalExpr expr) { + if (expr.isDef()) { + sideEffects |= SideEffectChecker.STORE; + } + expr.visitChildren(this); + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + sideEffects |= SideEffectChecker.THROW; + expr.visitChildren(this); + } + + public void visitRCExpr(final RCExpr expr) { + sideEffects |= SideEffectChecker.RC; + expr.visitChildren(this); + } + + public void visitUCExpr(final UCExpr expr) { + sideEffects |= SideEffectChecker.UC; + expr.visitChildren(this); + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + sideEffects |= SideEffectChecker.THROW | SideEffectChecker.ALLOC; + expr.visitChildren(this); + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + // Memory allocation + // NegativeArraySizeException + sideEffects |= SideEffectChecker.THROW | SideEffectChecker.ALLOC; + expr.visitChildren(this); + } + + public void visitCatchExpr(final CatchExpr expr) { + // Stack change + sideEffects |= SideEffectChecker.STACK; + expr.visitChildren(this); + } + + public void visitNewExpr(final NewExpr expr) { + // Memory allocation + sideEffects |= SideEffectChecker.ALLOC; + expr.visitChildren(this); + } + + public void visitStackExpr(final StackExpr expr) { + // Stack change + sideEffects |= SideEffectChecker.STACK; + + if (expr.isDef()) { + sideEffects |= SideEffectChecker.STORE; + } + + expr.visitChildren(this); + } + + public void visitCastExpr(final CastExpr expr) { + // ClassCastException + if (expr.castType().isReference()) { + sideEffects |= SideEffectChecker.THROW; + } + expr.visitChildren(this); + } + + public void visitArithExpr(final 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(final ArrayLengthExpr expr) { + // NullPointerException + sideEffects |= SideEffectChecker.THROW; + expr.visitChildren(this); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + // NullPointerException, ArrayIndexOutOfBoundsException, + // ArrayStoreException + sideEffects |= SideEffectChecker.THROW; + + if (expr.isDef()) { + sideEffects |= SideEffectChecker.STORE; + } + + sideEffects |= SideEffectChecker.ALIAS; + + expr.visitChildren(this); + } + + public void visitFieldExpr(final FieldExpr expr) { + // NullPointerException -- handled by ZeroCheckExpr + /* + * sideEffects |= THROW; + */ + + if (expr.isDef()) { + sideEffects |= SideEffectChecker.STORE; + } + + final MemberRef field = expr.field(); + + try { + final FieldEditor e = context.editField(field); + + if (!e.isFinal()) { + sideEffects |= SideEffectChecker.ALIAS; + } + + if (e.isVolatile()) { + sideEffects |= SideEffectChecker.VOLATILE; + } + + context.release(e.fieldInfo()); + } catch (final NoSuchFieldException e) { + // A field wasn't found. Silently assume it's not final and + // is volatile. + sideEffects |= SideEffectChecker.ALIAS; + sideEffects |= SideEffectChecker.VOLATILE; + } + + expr.visitChildren(this); + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + if (expr.isDef()) { + sideEffects |= SideEffectChecker.STORE; + } + + final MemberRef field = expr.field(); + + try { + final FieldEditor e = context.editField(field); + + if (e.isVolatile()) { + sideEffects |= SideEffectChecker.VOLATILE; + } + + context.release(e.fieldInfo()); + } catch (final NoSuchFieldException e) { + // A field wasn't found. Silently assume it's volatile. + sideEffects |= SideEffectChecker.VOLATILE; + } + + expr.visitChildren(this); + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + // Call + sideEffects |= SideEffectChecker.THROW | SideEffectChecker.CALL; + expr.visitChildren(this); + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + // Call + sideEffects |= SideEffectChecker.THROW | SideEffectChecker.CALL; + expr.visitChildren(this); + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + // Synchronization + sideEffects |= SideEffectChecker.THROW | SideEffectChecker.SYNC; + stmt.visitChildren(this); + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + // Stack change + sideEffects |= SideEffectChecker.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..47d1a9b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/StackOpt.java @@ -0,0 +1,1331 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import EDU.purdue.cs.bloat.editor.*; + +public class StackOpt extends InstructionAdapter implements Opcode { + + int stackHeight; + + int minStackHeight; + + UseMap uMap; + + static final boolean DEBUG = false; + + public void transform(final MethodEditor method) { + + uMap = method.uMap(); + + for (int i = method.codeLength() - 1; i > 0; i--) { + + Instruction inst; + boolean isWide; + final Object codeEl = method.codeElementAt(i); + + if ((codeEl instanceof Instruction) + && ((Instruction) codeEl).isLoad()) { + inst = (Instruction) codeEl; + } else { + continue; + } + + switch (inst.opcodeClass()) { + + case opcx_iload: + case opcx_fload: + case opcx_aload: + case opcx_iaload: + case opcx_faload: + case opcx_aaload: + case opcx_baload: + case opcx_caload: + case opcx_saload: + isWide = false; + break; + case opcx_lload: + case opcx_dload: + case opcx_laload: + case opcx_daload: + default: + isWide = true; + } + + stackHeight = 0; + + for (int j = i - 1;; j--) { + + // stop at the begining of the code or a basic block. + if ((j <= 0) || // this should be redundant, but to be safe + (method.codeElementAt(j) instanceof Label)) { + break; + } + + if ((stackHeight == -1) + && ((uMap.hasSameDef(inst, ((Instruction) method + .codeElementAt(j))) && ((Instruction) method + .codeElementAt(j)).isLoad()) || dupRun(method, + j, inst))) { + + if (forwardCountCheck(method, j, i, -1)) { + // found a type 0 relation with a load + if (StackOpt.DEBUG) { + System.err + .println("load type 0: " + + ((Instruction) method + .codeElementAt(j)) + .toString() + " " + + inst.toString()); + } + + if (isWide) { + method.insertCodeAt( + new Instruction(Opcode.opc_dup2), j + 1); + } else { + method.insertCodeAt( + new Instruction(Opcode.opc_dup), j + 1); // add + // dup + } + i++; // code has changed; why don't method editors + // have iterators? + method.removeCodeAt(i); // remove load + } + break; // done, even if final check failed. + } + + else if ((stackHeight == 0) + && uMap.hasSameDef(inst, ((Instruction) method + .codeElementAt(j)))) { + if (((Instruction) method.codeElementAt(j)).isStore()) { + + if (forwardCountCheck(method, j, i, 0)) { + // found a type 0 with a store + if (StackOpt.DEBUG) { + System.err.println("store type 0: " + + ((Instruction) method + .codeElementAt(j)).toString() + + " " + inst.toString()); + } + + if (isWide) { + method.insertCodeAt(new Instruction( + Opcode.opc_dup2), j); + } else { + method.insertCodeAt(new Instruction( + Opcode.opc_dup), j); + } + i++; + method.removeCodeAt(i); + } + break; + } else if (((Instruction) method.codeElementAt(j)).isLoad() + && !isWide) { // can't do type 1s with wides. + + if (forwardCountCheck(method, j, i, -1)) { + // found a type 1 with a load + if (StackOpt.DEBUG) { + System.err.println("load type 1: " + + ((Instruction) method + .codeElementAt(j)).toString() + + " " + inst.toString()); + } + + method.insertCodeAt( + new Instruction(Opcode.opc_dup), j + 1); + i++; + method.replaceCodeAt(new Instruction( + Opcode.opc_swap), i); + } + break; + } + } + + else if ((stackHeight == 1) + && uMap.hasSameDef(inst, ((Instruction) method + .codeElementAt(j)))) { + if (((Instruction) method.codeElementAt(j)).isStore() + && !isWide) { // can't do type 1 with wides + + if (forwardCountCheck(method, j, i, 0)) { + // type 1 for stores + if (StackOpt.DEBUG) { + System.err.println("store type 1: " + + ((Instruction) method + .codeElementAt(j)).toString() + + " " + inst.toString()); + } + + method.insertCodeAt( + new Instruction(Opcode.opc_dup), j); + i++; + method.replaceCodeAt(new Instruction( + Opcode.opc_swap), i); + } + break; + } + } + + heightChange(method.codeElementAt(j)); + // System.err.print(stackHeight + ";"); + } + } + + } + + boolean forwardCountCheck(final MethodEditor m, final int j, final int i, + final int bound) { + + stackHeight = 0; + minStackHeight = 0; + + for (int k = j + 1; k < i; k++) { + heightChange(m.codeElementAt(k)); + if (minStackHeight < bound) { + return false; + } + } + + return true; + } + + boolean dupRun(final MethodEditor m, final int j, final Instruction inst) { + + if (((Instruction) m.codeElementAt(j)).opcodeClass() == Opcode.opcx_dup) { + for (int k = j - 1;; k--) { + if (m.codeElementAt(k) instanceof Instruction) { + if (((Instruction) m.codeElementAt(k)).opcodeClass() == Opcode.opcx_dup) { + continue; + } else if (((Instruction) m.codeElementAt(k)).isLoad() + && uMap.hasSameDef(inst, ((Instruction) m + .codeElementAt(k)))) { + return true; + } + } + break; + } + } + + return false; + } + + void heightChange(final Object inst) { + + if (inst instanceof Instruction) { + ((Instruction) inst).visit(this); + } + + } + + public void visit_nop(final Instruction inst) { + stackHeight += 0; + } + + public void visit_ldc(final Instruction inst) { + final Object operand = inst.operand(); + + if ((operand instanceof Long) || (operand instanceof Double)) { + stackHeight += 2; + + } else { + stackHeight += 1; + } + } + + public void visit_iload(final Instruction inst) { + stackHeight += 1; + } + + public void visit_lload(final Instruction inst) { + stackHeight += 2; + } + + public void visit_fload(final Instruction inst) { + stackHeight += 1; + } + + public void visit_dload(final Instruction inst) { + stackHeight += 2; + } + + public void visit_aload(final Instruction inst) { + stackHeight += 1; + } + + public void visit_iaload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_laload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_faload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_daload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_aaload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_baload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_caload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_saload(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_istore(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_lstore(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_fstore(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_dstore(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_astore(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_iastore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_lastore(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_fastore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_dastore(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_aastore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_bastore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_castore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_sastore(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_pop(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_pop2(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_dup(final Instruction inst) { + stackHeight += 1; + } + + public void visit_dup_x1(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 3; + } + + public void visit_dup_x2(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 4; + } + + public void visit_dup2(final Instruction inst) { + stackHeight += 2; + } + + public void visit_dup2_x1(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 5; + } + + public void visit_dup2_x2(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 6; + } + + public void visit_swap(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_iadd(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + + } + + public void visit_ladd(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_fadd(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dadd(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_isub(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lsub(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_fsub(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dsub(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_imul(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lmul(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_fmul(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dmul(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_idiv(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_ldiv(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_fdiv(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_ddiv(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_irem(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lrem(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_frem(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_drem(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_ineg(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lneg(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_fneg(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dneg(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + + } + + public void visit_ishl(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lshl(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_ishr(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lshr(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_iushr(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lushr(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_iand(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_land(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_ior(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lor(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_ixor(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lxor(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_iinc(final Instruction inst) { + + } + + public void visit_i2l(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 2; + } + + public void visit_i2f(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_i2d(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 2; + } + + public void visit_l2i(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_l2f(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_l2d(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_f2i(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_f2l(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 2; + } + + public void visit_f2d(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 2; + } + + public void visit_d2i(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_d2l(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 2; + } + + public void visit_d2f(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_i2b(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_i2c(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_i2s(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_lcmp(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_fcmpl(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_fcmpg(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dcmpl(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_dcmpg(final Instruction inst) { + stackHeight -= 4; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_ifeq(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_ifne(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_iflt(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_ifge(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_ifgt(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_ifle(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmpeq(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmpne(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmplt(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmpge(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmpgt(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_icmple(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_if_acmpeq(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_if_acmpne(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_goto(final Instruction inst) { + + } + + public void visit_jsr(final Instruction inst) { + stackHeight += 1; + } + + public void visit_ret(final Instruction inst) { + } + + public void visit_switch(final Instruction inst) { + stackHeight -= 1; + } + + public void visit_ireturn(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_lreturn(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_freturn(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_dreturn(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_areturn(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_return(final Instruction inst) { + } + + public void visit_getstatic(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight += type.stackHeight(); + } + + public void visit_putstatic(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight(); + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_putstatic_nowb(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight(); + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_getfield(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += type.stackHeight(); + } + + public void visit_putfield(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight() + 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_putfield_nowb(final Instruction inst) { + final Type type = ((MemberRef) inst.operand()).nameAndType().type(); + stackHeight -= type.stackHeight() + 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_invokevirtual(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight -= type.stackHeight() + 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += type.returnType().stackHeight(); + } + + public void visit_invokespecial(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight -= type.stackHeight() + 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += type.returnType().stackHeight(); + + } + + public void visit_invokestatic(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight -= type.stackHeight(); + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += type.returnType().stackHeight(); + } + + public void visit_invokeinterface(final Instruction inst) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + stackHeight -= type.stackHeight() + 1; + + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += type.returnType().stackHeight(); + } + + public void visit_new(final Instruction inst) { + stackHeight += 1; + } + + public void visit_newarray(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + + } + + public void visit_arraylength(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + } + + public void visit_athrow(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_checkcast(final Instruction inst) { + + } + + public void visit_instanceof(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + stackHeight += 1; + + } + + public void visit_monitorenter(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_monitorexit(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + } + + public void visit_multianewarray(final Instruction inst) { + final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); + final int dim = operand.dimensions(); + + stackHeight -= dim; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + + stackHeight += 1; + } + + public void visit_ifnull(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_ifnonnull(final Instruction inst) { + stackHeight -= 1; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_rc(final Instruction inst) { + } + + public void visit_aupdate(final Instruction inst) { + } + + public void visit_supdate(final Instruction inst) { + } + + public void visit_aswizzle(final Instruction inst) { + stackHeight -= 2; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + + public void visit_aswrange(final Instruction inst) { + stackHeight -= 3; + if (stackHeight < minStackHeight) { + minStackHeight = stackHeight; + } + } + +} diff --git a/src/EDU/purdue/cs/bloat/trans/StackPRE.java b/src/EDU/purdue/cs/bloat/trans/StackPRE.java new file mode 100644 index 0000000..bc20233 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/StackPRE.java @@ -0,0 +1,1518 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * Eliminate partially redundant local variable loads and stores by replacing + * them with stack variables and dups. + * + * The algorithm is similar to SSAPRE, except: + * + * We need to place phis for locals at the IDF of the blocks containing defs and + * uses (not just defs). + */ +public class StackPRE { + public static boolean DEBUG = false; + + protected FlowGraph cfg; + + protected List[] varphis; + + protected List[] stackvars; + + protected Worklist worklist; + + int next = 0; + + public StackPRE(final FlowGraph cfg) { + this.cfg = cfg; + } + + public void transform() { + stackvars = new ArrayList[cfg.size()]; + + for (int i = 0; i < stackvars.length; i++) { + stackvars[i] = new ArrayList(); + } + + varphis = new ArrayList[cfg.size()]; + + for (int i = 0; i < varphis.length; i++) { + varphis[i] = new ArrayList(); + } + + // Collect local and stack variables into a worklist. + // Mark the variables that are pushed before any are popped. + worklist = new Worklist(); + + cfg.visit(new TreeVisitor() { + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + worklist.addVarPhi(stmt); + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + worklist.addLocalVar((LocalExpr) stmt.target()); + } + + public void visitLocalExpr(final LocalExpr expr) { + worklist.addLocalVar(expr); + } + + public void visitStackExpr(final StackExpr expr) { + worklist.addStackVar(expr); + } + }); + + while (!worklist.isEmpty()) { + final ExprInfo exprInfo = worklist.removeFirst(); + + if (StackPRE.DEBUG) { + System.out.println("PRE for " + exprInfo.def() + + " -------------------------"); + System.out.println("Placing Phis for " + exprInfo.def() + + " -------------------------"); + } + + placePhiFunctions(exprInfo); + + if (StackPRE.DEBUG) { + exprInfo.print(); + System.out.println("Renaming for " + exprInfo.def() + + " -------------------------"); + } + + rename(exprInfo); + + if (StackPRE.DEBUG) { + exprInfo.print(); + System.out.println("Down safety for " + exprInfo.def() + + " -------------------------"); + } + + downSafety(exprInfo); + + if (StackPRE.DEBUG) { + System.out.println("Will be available for " + exprInfo.def() + + " -------------------------"); + } + + willBeAvail(exprInfo); + + if (StackPRE.DEBUG) { + System.out.println("Finalize for " + exprInfo.def() + + " -------------------------"); + } + + finalize(exprInfo); + + if (StackPRE.DEBUG) { + System.out.println("Code motion for " + exprInfo.def() + + " -------------------------"); + } + + final Type type = exprInfo.def().type(); + final StackExpr tmp = new StackExpr(0, type); + final SSAConstructionInfo consInfo = new SSAConstructionInfo(cfg, + tmp); + + codeMotion(exprInfo, tmp, consInfo); + + if (StackPRE.DEBUG) { + System.out.println("Performing incremental SSA for " + + exprInfo.def() + " -------------------------"); + } + + SSA.transform(cfg, consInfo); + + // Get the stack variable phis. + final Collection defBlocks = consInfo.defBlocks(); + final Iterator e = cfg.iteratedDomFrontier(defBlocks).iterator(); + + while (e.hasNext()) { + final Block block = (Block) e.next(); + + final Iterator stmts = block.tree().stmts().iterator(); + + while (stmts.hasNext()) { + final Stmt stmt = (Stmt) stmts.next(); + if (stmt instanceof PhiJoinStmt) { + worklist.prependVarPhi((PhiJoinStmt) stmt); + } else if (!(stmt instanceof LabelStmt)) { + // Only labels occur before phis. If we hit + // something else, there are no more phis. + break; + } + } + } + + if (StackPRE.DEBUG) { + exprInfo.print(); + System.out.println("Done with PRE for " + exprInfo.def() + + " -------------------------"); + } + + exprInfo.cleanup(); + } + + varphis = null; + worklist = null; + } + + /** + * For an local variable, v, insert a Phi at the iterated dominance frontier + * of the blocks containing defs and uses of v. This differs from SSA phi + * placement in that uses, not just defs are considered in computing the + * IDF. + */ + private void placePhiFunctions(final ExprInfo exprInfo) { + final ArrayList w = new ArrayList(cfg.size()); + + final Iterator uses = exprInfo.def().uses().iterator(); + + w.add(exprInfo.def().block()); + + while (uses.hasNext()) { + final LocalExpr use = (LocalExpr) uses.next(); + + if (use.parent() instanceof PhiJoinStmt) { + final PhiJoinStmt phi = (PhiJoinStmt) use.parent(); + + final Iterator preds = cfg.preds(use.block()).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + + if (phi.operandAt(pred) == use) { + w.add(pred); + break; + } + } + } else if (!(use.parent() instanceof PhiCatchStmt)) { + w.add(use.block()); + } + } + + final Iterator df = cfg.iteratedDomFrontier(w).iterator(); + + while (df.hasNext()) { + final Block block = (Block) df.next(); + exprInfo.addPhi(block); + } + + // Don't bother with placing phis for catch blocks, since the + // operand stack is zeroed at catch blocks. + } + + /** + * Set the definition for the variable occurences. After this step all + * occurences of the variable which are at different heights will have + * different definitions. + */ + private void rename(final ExprInfo exprInfo) { + search(cfg.source(), exprInfo, null, 0, false); + } + + private void search(final Block block, final ExprInfo exprInfo, Def top, + int totalBalance, boolean seenDef) { + if (StackPRE.DEBUG) { + System.out.println(" renaming in " + block); + } + + if (cfg.catchBlocks().contains(block)) { + if (top != null) { + top.setDownSafe(false); + } + + top = null; + } + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + if (top != null) { + top.setDownSafe(false); + } + + top = phi; + + if (!seenDef) { + top.setDownSafe(false); + } + } + + Node parent = null; + int balance = 0; + + final Iterator iter = exprInfo.varsAtBlock(block).iterator(); + + while (iter.hasNext()) { + final VarExpr node = (VarExpr) iter.next(); + + // Get the parent of the node. If the parent is a putfield + // or array store, then the node is popped when the grandparent + // is evaluated, not when the parent is evaluated. + // We keep track of the parent so that when it changes, we + // know to update the operand stack balance. + + Node p = node.parent(); + + if ((p instanceof MemRefExpr) && ((MemRefExpr) p).isDef()) { + p = p.parent(); + } + + if (parent != p) { + parent = p; + totalBalance += balance; + balance = 0; + + if ((top != null) && (totalBalance < 0)) { + top.setDownSafe(false); + } + } + + if (node instanceof StackExpr) { + if (parent instanceof StackManipStmt) { + switch (((StackManipStmt) parent).kind()) { + case StackManipStmt.DUP: + case StackManipStmt.DUP_X1: + case StackManipStmt.DUP_X2: + balance += 1; + break; + case StackManipStmt.DUP2: + case StackManipStmt.DUP2_X1: + case StackManipStmt.DUP2_X2: + balance += 2; + break; + default: + break; + } + } else if (node.isDef()) { + balance += node.type().stackHeight(); + } else { + balance -= node.type().stackHeight(); + } + } else { + final LocalExpr var = (LocalExpr) node; + + if (var.isDef()) { + seenDef = true; + } + + if (StackPRE.DEBUG) { + System.out.println("node = " + var + " in " + parent); + } + + if ((totalBalance == 0) && onBottom(var, false)) { + // Copy the def from the top of the stack and + // create a new def. + exprInfo.setDef(var, top); + top = new RealDef(var); + + if ((balance != 0) || !onBottom(var, true)) { + top.setDownSafe(false); + } + + if (StackPRE.DEBUG) { + System.out.println("New def " + top + " with balance " + + totalBalance + " + " + balance); + } + } else { + // The occurence is not on the bottom, so it + // must be reloaded from a local. + exprInfo.setDef(var, null); + } + } + + if (StackPRE.DEBUG) { + System.out.println("after " + parent + " top = " + top); + } + } + + totalBalance += balance; + + if ((top != null) && (totalBalance < 0)) { + top.setDownSafe(false); + } + + // If we hit the sink node, a def at the top of the stack is not + // down safe. + if ((block == cfg.sink()) || cfg.succs(block).contains(cfg.sink())) { + if (top != null) { + top.setDownSafe(false); + } + } + + // First, fill in the operands for the StackPRE phis. Then, + // handle local variable occurences in successor block variable + // phis. We do this after the StackPRE phis since they will + // hoist code above the variable phis. + + Iterator succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + if (succPhi != null) { + succPhi.setOperandAt(block, top); + } + } + + succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Iterator phis = varPhisAtBlock(succ).iterator(); + + while (phis.hasNext()) { + final PhiJoinStmt stmt = (PhiJoinStmt) phis.next(); + + final Expr operand = stmt.operandAt(block); + + if (operand instanceof StackExpr) { + balance += operand.type().stackHeight(); + } + + if (stmt.target() instanceof StackExpr) { + balance -= stmt.target().type().stackHeight(); + + if (top != null) { + top.setDownSafe(false); + top = null; + } + } + + if ((operand != null) && (operand.def() == exprInfo.def())) { + // Phi operands aren't allowed to define any of the + // locals. This should never happen since none of the + // locals should be dominated by the phi operand, + // but we'll play it safe and set top to null. + exprInfo.setDef((LocalExpr) operand, top); + top = null; + } + + if (stmt.target() == exprInfo.def()) { + exprInfo.setDef((LocalExpr) stmt.target(), top); + top = new RealDef((LocalExpr) stmt.target()); + } + + totalBalance += balance; + + if ((top != null) && (totalBalance < 0)) { + top.setDownSafe(false); + } + } + } + + final Iterator children = cfg.domChildren(block).iterator(); + + while (children.hasNext()) { + final Block child = (Block) children.next(); + search(child, exprInfo, top, totalBalance, seenDef); + } + } + + private boolean onBottom(final LocalExpr var, final boolean really) { + // InitStmts and PhiStmts are always on the bottom. + if ((var.stmt() instanceof InitStmt) || (var.stmt() instanceof PhiStmt)) { + return true; + } + + class Bool { + boolean value = true; + } + ; + + final Bool bottom = new Bool(); + + var.stmt().visitChildren(new TreeVisitor() { + boolean seen = false; + + public void visitExpr(final Expr expr) { + if (StackPRE.DEBUG) { + System.out.println("Checking " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + + if (!seen) { + expr.visitChildren(this); + } + + if (!seen) { + bottom.value = false; + seen = true; + } + + if (StackPRE.DEBUG) { + System.out.println("Done with " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + } + + public void visitLocalExpr(final LocalExpr expr) { + if (StackPRE.DEBUG) { + System.out.println("Checking " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + + if (!seen) { + if (expr == var) { + seen = true; + } else if (expr.def() != var.def()) { + bottom.value = false; + seen = true; + } + } + + if (StackPRE.DEBUG) { + System.out.println("Done with " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + } + + public void visitStackExpr(final StackExpr expr) { + if (StackPRE.DEBUG) { + System.out.println("Checking " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + + if (really && !seen) { + bottom.value = false; + seen = true; + } + + if (StackPRE.DEBUG) { + System.out.println("Done with " + expr + " seen=" + seen + + " bottom=" + bottom.value); + } + } + }); + + return bottom.value; + } + + /** + * Mark each def as 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 refinition 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(final ExprInfo exprInfo) { + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (StackPRE.DEBUG) { + System.out.println(" down safety for " + phi + " in " + + block); + } + + if (phi.downSafe()) { + if (StackPRE.DEBUG) { + System.out.println(" already down safe"); + } + + continue; + } + + // The phi is not down safe. Make all its operands not + // down safe. + + final Iterator e = phi.operands().iterator(); + + while (e.hasNext()) { + final Def def = (Def) e.next(); + + if (def != null) { + resetDownSafe(def); + } + } + } + } + + private void resetDownSafe(final Def def) { + if (StackPRE.DEBUG) { + System.out.println(" reset down safe for " + def); + } + + if (def instanceof Phi) { + final Phi phi = (Phi) def; + + if (phi.downSafe()) { + phi.setDownSafe(false); + + final Iterator e = phi.operands().iterator(); + + while (e.hasNext()) { + final Def operand = (Def) e.next(); + + if (operand != null) { + resetDownSafe(operand); + } + } + } + } else { + def.setDownSafe(false); + } + } + + /** + * Predict whether the expression will be available at each Phi result + * following insertions for PRE. + */ + private void willBeAvail(final ExprInfo exprInfo) { + computeCanBeAvail(exprInfo); + computeLater(exprInfo); + } + + private void computeCanBeAvail(final ExprInfo exprInfo) { + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi == null) { + continue; + } + + if (!phi.downSafe() && phi.canBeAvail()) { + resetCanBeAvail(exprInfo, phi); + } + } + } + + private void resetCanBeAvail(final ExprInfo exprInfo, final Phi phi) { + phi.setCanBeAvail(false); + + final Iterator blocks = cfg.nodes().iterator(); + + // For each phi whose operand is at + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + final Iterator e = cfg.preds(other.block()).iterator(); + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + + final Def def = other.operandAt(pred); + + if (def == phi) { + other.setOperandAt(pred, null); + + if (!other.downSafe() && other.canBeAvail()) { + resetCanBeAvail(exprInfo, other); + } + } + } + } + } + + private void computeLater(final ExprInfo exprInfo) { + Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if (phi != null) { + phi.setLater(phi.canBeAvail()); + } + } + + blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi phi = exprInfo.exprPhiAtBlock(block); + + if ((phi != null) && phi.later()) { + final Iterator e = phi.operands().iterator(); + + while (e.hasNext()) { + final Def def = (Def) e.next(); + + if (def instanceof RealDef) { + resetLater(exprInfo, phi); + break; + } + } + } + } + } + + private void resetLater(final ExprInfo exprInfo, final Phi phi) { + phi.setLater(false); + + final Iterator blocks = cfg.nodes().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + final Phi other = exprInfo.exprPhiAtBlock(block); + + if (other == null) { + continue; + } + + final Iterator e = other.operands().iterator(); + + while (e.hasNext()) { + final Def def = (Def) e.next(); + + if ((def == phi) && other.later()) { + resetLater(exprInfo, other); + break; + } + } + } + } + + private void finalize(final ExprInfo exprInfo) { + final Iterator uses = exprInfo.def().uses().iterator(); + + while (uses.hasNext()) { + final LocalExpr use = (LocalExpr) uses.next(); + + if (use.parent() instanceof PhiCatchStmt) { + exprInfo.setSave(true); + break; + } + } + + finalizeVisit(exprInfo, cfg.source()); + } + + private void finalizeVisit(final ExprInfo exprInfo, final Block block) { + if (StackPRE.DEBUG) { + System.out.println(" finalizing " + block); + } + + // First finalize normal occurences of the local. + final Iterator reals = exprInfo.varsAtBlock(block).iterator(); + + while (reals.hasNext()) { + final VarExpr node = (VarExpr) reals.next(); + + if (node instanceof LocalExpr) { + final LocalExpr real = (LocalExpr) node; + + if (StackPRE.DEBUG) { + System.out.println(" -----------"); + } + + final Def def = exprInfo.def(real); + + if ((def != null) && def.downSafe()) { + // We can reload from a stack variable, unless the we + // can't safely push the phi operands. + if (def instanceof Phi) { + if (((Phi) def).willBeAvail()) { + exprInfo.setPop(real, true); + } else { + exprInfo.setSave(true); + } + } else { + exprInfo.setPush(((RealDef) def).var, true); + exprInfo.setPop(real, true); + } + } else { + // The real is not on the bottom. We must reload from a + // local variable. + if (real != exprInfo.def()) { + exprInfo.setSave(true); + } + } + } + } + + // Next, handle code motion. + Iterator succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + if ((succPhi != null) && succPhi.willBeAvail()) { + if (succPhi.insert(block)) { + succPhi.setPushOperand(block, true); + } else { + final Def def = succPhi.operandAt(block); + + if (def instanceof RealDef) { + Assert.isTrue(def.downSafe(), succPhi + " operand for " + + block + " is not DS: " + def); + exprInfo.setPush(((RealDef) def).var, true); + } else { + Assert.isTrue(def instanceof Phi, succPhi + + " operand for " + block + " is not a phi: " + + def); + Assert.isTrue(((Phi) def).willBeAvail(), succPhi + + " operand for " + block + " is not WBA: " + + def); + } + } + } + } + + // Lastly, finalize occurences in variable phis. We do this + // after the StackPRE hoisting since the hoisted code will + // occur before the phis. + succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Iterator phis = varPhisAtBlock(succ).iterator(); + + while (phis.hasNext()) { + final PhiJoinStmt stmt = (PhiJoinStmt) phis.next(); + + final Expr operand = stmt.operandAt(block); + + if ((operand != null) && (operand.def() == exprInfo.def())) { + final LocalExpr var = (LocalExpr) operand; + final Def def = exprInfo.def(var); + + if ((def != null) && def.downSafe()) { + // We can reload from a stack variable, unless the we + // can't safely push the phi operands. + if (def instanceof Phi) { + if (((Phi) def).willBeAvail()) { + exprInfo.setPop(var, true); + } else { + exprInfo.setSave(true); + } + } else { + exprInfo.setPush(((RealDef) def).var, true); + exprInfo.setPop(var, true); + } + } + } + } + } + + final Iterator children = cfg.domChildren(block).iterator(); + + while (children.hasNext()) { + final Block child = (Block) children.next(); + finalizeVisit(exprInfo, child); + } + } + + private void codeMotion(final ExprInfo exprInfo, final StackExpr tmp, + final SSAConstructionInfo consInfo) { + // Be sure to visit pre-order so at least one predecessor is visited + // before each block. + final Iterator blocks = cfg.preOrder().iterator(); + + while (blocks.hasNext()) { + final Block block = (Block) blocks.next(); + + if ((block == cfg.source()) || (block == cfg.sink())) { + continue; + } + + boolean added = false; + + final Iterator reals = exprInfo.varsAtBlock(block).iterator(); + + while (reals.hasNext()) { + final VarExpr node = (VarExpr) reals.next(); + + if (node instanceof LocalExpr) { + final LocalExpr var = (LocalExpr) node; + + // If marked push, save it to a stack variable. + // If marked pop, reload from a stack variable. + + final boolean push = exprInfo.push(var); + boolean pop = exprInfo.pop(var); + + if (var.isDef() && exprInfo.save()) { + pop = false; + } + + if (push && pop) { + Assert.isTrue(var != exprInfo.def()); + + final StackExpr t1 = (StackExpr) tmp.clone(); + final StackExpr t2 = (StackExpr) tmp.clone(); + + final StoreExpr store = new StoreExpr(t1, t2, t2.type()); + var.replaceWith(store); + + consInfo.addReal(t2); + consInfo.addReal(t1); + added = true; + } else if (push) { + final StackExpr t1 = (StackExpr) tmp.clone(); + + final LocalExpr t2 = (LocalExpr) var.clone(); + t2.setDef(exprInfo.def()); + + final StoreExpr store = new StoreExpr(t1, t2, t2.type()); + + if (var != exprInfo.def()) { + var.replaceWith(store); + } else { + final Node parent = var.parent(); + + if (parent instanceof Stmt) { + // InitStmt or PhiStmt. + final Stmt stmt = new ExprStmt(store); + block.tree().addStmtAfter(stmt, (Stmt) parent); + } else { + // a := E -> a := (S := E) + Assert.isTrue(parent instanceof StoreExpr); + final Expr rhs = ((StoreExpr) parent).expr(); + parent.visit(new ReplaceVisitor(rhs, store)); + store.visit(new ReplaceVisitor(t2, rhs)); + t2.cleanup(); + } + } + + consInfo.addReal(t1); + added = true; + } else if (pop) { + final StackExpr t1 = (StackExpr) tmp.clone(); + var.replaceWith(t1); + + consInfo.addReal(t1); + added = true; + } + } + } + + final List s = stackvars[cfg.preOrderIndex(block)]; + + if (added) { + s.clear(); + + block.tree().visitChildren(new TreeVisitor() { + public void visitStackExpr(final StackExpr expr) { + s.add(expr); + } + }); + } + + Iterator succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Phi succPhi = exprInfo.exprPhiAtBlock(succ); + + if ((succPhi != null) && succPhi.pushOperand(block)) { + final StackExpr t1 = (StackExpr) tmp.clone(); + final LocalExpr t2 = (LocalExpr) exprInfo.def().clone(); + t2.setDef(exprInfo.def()); + + final StoreExpr store = new StoreExpr(t1, t2, t1.type()); + + block.tree().addStmtBeforeJump(new ExprStmt(store)); + + s.add(t1); + + consInfo.addReal(t1); + + if (StackPRE.DEBUG) { + System.out.println("insert at end of " + block + ": " + + store); + } + } + } + + succs = cfg.succs(block).iterator(); + + while (succs.hasNext()) { + final Block succ = (Block) succs.next(); + + final Iterator phis = varPhisAtBlock(succ).iterator(); + + while (phis.hasNext()) { + final PhiJoinStmt stmt = (PhiJoinStmt) phis.next(); + + final Expr operand = stmt.operandAt(block); + + if ((operand != null) && (operand.def() == exprInfo.def())) { + final LocalExpr var = (LocalExpr) operand; + + Assert.isFalse(exprInfo.push(var)); + + if (exprInfo.pop(var)) { + final StackExpr t1 = (StackExpr) tmp.clone(); + var.replaceWith(t1); + consInfo.addReal(t1); + } + } + } + } + } + } + + abstract class Def { + int version; + + boolean downSafe; + + public Def() { + this.version = next++; + this.downSafe = true; + } + + public void setDownSafe(final boolean flag) { + if (StackPRE.DEBUG) { + System.out.println(this + " DS = " + flag); + } + + downSafe = flag; + } + + public boolean downSafe() { + return downSafe; + } + } + + class RealDef extends Def { + LocalExpr var; + + public RealDef(final LocalExpr var) { + this.var = var; + + if (StackPRE.DEBUG) { + System.out + .println("new def for " + var + " in " + var.parent()); + } + } + + public LocalExpr var() { + return var; + } + + public String toString() { + return var.toString() + "{" + version + "," + + (downSafe() ? "" : "!") + "DS}"; + } + } + + class Phi extends Def { + Block block; + + HashMap operands; + + HashMap saveOperand; + + boolean live; + + boolean downSafe; + + boolean canBeAvail; + + boolean later; + + public Phi(final Block block) { + this.block = block; + + operands = new HashMap(cfg.preds(block).size() * 2); + saveOperand = new HashMap(cfg.preds(block).size() * 2); + + downSafe = true; + canBeAvail = true; + later = true; + } + + public Block block() { + return block; + } + + public Collection operands() { + return new AbstractCollection() { + public int size() { + return cfg.preds(block).size(); + } + + public boolean contains(final Object obj) { + if (obj == null) { + return operands.size() != cfg.preds(block).size(); + } + + return operands.containsValue(obj); + } + + public Iterator iterator() { + final Iterator iter = cfg.preds(block).iterator(); + + return new Iterator() { + public boolean hasNext() { + return iter.hasNext(); + } + + public Object next() { + final Block block = (Block) iter.next(); + return operandAt(block); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + public Def operandAt(final Block block) { + return (Def) operands.get(block); + } + + public void setOperandAt(final Block block, final Def def) { + if (def != null) { + operands.put(block, def); + } else { + operands.remove(block); + } + } + + public void setPushOperand(final Block block, final boolean flag) { + if (StackPRE.DEBUG) { + System.out.println(" operand " + block + " save=" + flag); + } + + saveOperand.put(block, new Boolean(flag)); + } + + public boolean pushOperand(final Block block) { + final Boolean flag = (Boolean) saveOperand.get(block); + return (flag != null) && flag.booleanValue(); + } + + public boolean insert(final Block block) { + final Def def = operandAt(block); + + if (def == null) { + return true; + } + + if (!def.downSafe()) { + return true; + } + + if ((def instanceof Phi) && !((Phi) def).willBeAvail()) { + return true; + } + + return false; + } + + public boolean willBeAvail() { + return canBeAvail && !later; + } + + public void setCanBeAvail(final boolean flag) { + if (StackPRE.DEBUG) { + System.out.println(this + " CBA = " + flag); + } + + canBeAvail = flag; + } + + public boolean canBeAvail() { + return canBeAvail; + } + + public void setLater(final boolean flag) { + if (StackPRE.DEBUG) { + System.out.println(this + " Later = " + flag); + } + + later = flag; + } + + public boolean later() { + return later; + } + + public String toString() { + String s = ""; + + final Iterator iter = cfg.preds(block).iterator(); + + while (iter.hasNext()) { + final Block pred = (Block) iter.next(); + final Def def = operandAt(pred); + + s += pred.label() + "="; + + if (def == null) { + s += "null"; + } else { + s += def.version; + } + + if (iter.hasNext()) { + s += ", "; + } + } + + return "phi" + "{" + version + "," + (downSafe() ? "" : "!") + + "DS," + (canBeAvail() ? "" : "!") + "CBA," + + (later() ? "" : "!") + "Later}(" + s + ")"; + } + } + + public List varPhisAtBlock(final Block block) { + return varphis[cfg.preOrderIndex(block)]; + } + + /** + * Maintain the occurences so that they are visited in a preorder traversal + * of the dominator tree. + */ + private final class ExprInfo { + ArrayList[] vars; + + Phi[] phis; + + boolean save; + + Map pushes; + + Map pops; + + Map defs; + + LocalExpr def; + + ArrayList cleanup; + + public ExprInfo(final LocalExpr def) { + this.def = def; + + vars = new ArrayList[cfg.size()]; + + for (int i = 0; i < vars.length; i++) { + vars[i] = new ArrayList(); + } + + phis = new Phi[cfg.size()]; + + save = false; + + pushes = new HashMap(); + pops = new HashMap(); + + defs = new HashMap(); + + cleanup = new ArrayList(); + } + + public void cleanup() { + final Iterator iter = cleanup.iterator(); + + while (iter.hasNext()) { + final Node node = (Node) iter.next(); + node.cleanup(); + } + + vars = null; + phis = null; + pushes = null; + pops = null; + defs = null; + def = null; + cleanup = null; + } + + public void registerForCleanup(final Node node) { + cleanup.add(node); + } + + public void setSave(final boolean flag) { + save = flag; + } + + public boolean save() { + return save; + } + + public void setPush(final LocalExpr expr, final boolean flag) { + pushes.put(expr, new Boolean(flag)); + } + + public boolean push(final LocalExpr expr) { + final Boolean b = (Boolean) pushes.get(expr); + return (b != null) && b.booleanValue(); + } + + public void setPop(final LocalExpr expr, final boolean flag) { + pops.put(expr, new Boolean(flag)); + } + + public boolean pop(final LocalExpr expr) { + final Boolean b = (Boolean) pops.get(expr); + return (b != null) && b.booleanValue(); + } + + public void setDef(final LocalExpr expr, final Def def) { + if (StackPRE.DEBUG) { + System.out.println(" setting def for " + expr + " to " + + def); + } + + if (def != null) { + defs.put(expr, def); + } else { + defs.remove(expr); + } + } + + public Def def(final LocalExpr expr) { + final Def def = (Def) defs.get(expr); + + if (StackPRE.DEBUG) { + System.out.println(" def for " + expr + " is " + def); + } + + return def; + } + + public LocalExpr def() { + return def; + } + + public void addPhi(final Block block) { + Phi phi = phis[cfg.preOrderIndex(block)]; + + if (phi == null) { + if (StackPRE.DEBUG) { + System.out.println(" add phi for " + def + " at " + + block); + } + + phi = new Phi(block); + phis[cfg.preOrderIndex(block)] = phi; + } + } + + public List varsAtBlock(final Block block) { + final int blockIndex = cfg.preOrderIndex(block); + + final List list = new ArrayList(vars[blockIndex].size() + + stackvars[blockIndex].size()); + + final Iterator viter = vars[blockIndex].iterator(); + final Iterator siter = stackvars[blockIndex].iterator(); + + if (!viter.hasNext() && !siter.hasNext()) { + return new ArrayList(0); + } + + block.tree().visitChildren(new TreeVisitor() { + VarExpr vnext = null; + + VarExpr snext = null; + + { + if (viter.hasNext()) { + vnext = (VarExpr) viter.next(); + } + + if (siter.hasNext()) { + snext = (VarExpr) siter.next(); + } + } + + public void visitStmt(final Stmt stmt) { + if (((vnext != null) && (vnext.stmt() == stmt)) + || ((snext != null) && (snext.stmt() == stmt))) { + super.visitStmt(stmt); + } + } + + public void visitVarExpr(final VarExpr expr) { + super.visitExpr(expr); + + if (expr == vnext) { + if (viter.hasNext()) { + vnext = (VarExpr) viter.next(); + } else { + vnext = null; + } + + if (expr == snext) { + if (siter.hasNext()) { + snext = (VarExpr) siter.next(); + } else { + snext = null; + } + } + + list.add(expr); + } else if (expr == snext) { + if (siter.hasNext()) { + snext = (VarExpr) siter.next(); + } else { + snext = null; + } + + list.add(expr); + } + } + }); + + return list; + } + + public Phi exprPhiAtBlock(final Block block) { + return phis[cfg.preOrderIndex(block)]; + } + + protected void print() { + System.out.println("Print for " + def + "------------------"); + + cfg.visit(new PrintVisitor() { + Phi phi = null; + + public void visitBlock(final Block block) { + phi = exprPhiAtBlock(block); + super.visitBlock(block); + } + + public void visitLabelStmt(final LabelStmt stmt) { + super.visitLabelStmt(stmt); + + if (stmt.label().startsBlock()) { + if (phi != null) { + println(phi); + } + } + } + + public void visitLocalExpr(final LocalExpr expr) { + super.visitLocalExpr(expr); + + if (expr.def() == def) { + super.print("{" + defs.get(expr) + "}"); + } + } + }); + + System.out.println("End Print ----------------------------"); + } + } + + class Worklist { + Map exprInfos; + + LinkedList exprs; + + public Worklist() { + exprInfos = new HashMap(); + exprs = new LinkedList(); + } + + public boolean isEmpty() { + return exprs.isEmpty(); + } + + public ExprInfo removeFirst() { + final ExprInfo exprInfo = (ExprInfo) exprs.removeFirst(); + exprInfos.remove(exprInfo.def()); + return exprInfo; + } + + public void addLocalVar(final LocalExpr var) { + final int blockIndex = cfg.preOrderIndex(var.block()); + + if (StackPRE.DEBUG) { + System.out.println("add var " + var); + } + + ExprInfo exprInfo = (ExprInfo) exprInfos.get(var.def()); + + if (exprInfo == null) { + exprInfo = new ExprInfo((LocalExpr) var.def()); + exprs.add(exprInfo); + exprInfos.put(var.def(), exprInfo); + + if (StackPRE.DEBUG) { + System.out.println(" add info for " + var); + } + } + + exprInfo.vars[blockIndex].add(var); + } + + public void addStackVar(final StackExpr var) { + final int blockIndex = cfg.preOrderIndex(var.block()); + + if (StackPRE.DEBUG) { + System.out.println("add var " + var); + } + + stackvars[blockIndex].add(var); + } + + public void addVarPhi(final PhiJoinStmt stmt) { + varphis[cfg.preOrderIndex(stmt.block())].add(stmt); + } + + public void prependVarPhi(final PhiJoinStmt stmt) { + final List v = varphis[cfg.preOrderIndex(stmt.block())]; + + if (!v.contains(stmt)) { + v.add(0, stmt); + } + } + } +} 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..42c952e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/ValueFolder.java @@ -0,0 +1,1971 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.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(final boolean replace, final 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() { + final Iterator iter = clean.iterator(); + + while (iter.hasNext()) { + final 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(final Node node) { + } + + public void visitLocalExpr(final 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) { + final InitStmt stmt = (InitStmt) expr.parent(); + + final MethodEditor 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(final 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). + + final int v = stmt.valueNumber(); + int ov = -1; // Operand's value number + + final Iterator iter = stmt.operands().iterator(); + + // Examine each operand of the PhiJoinStmt. + while (iter.hasNext()) { + final 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); + final 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(final StoreExpr expr) { + if (expr.expr() instanceof CheckExpr) { + // This makes copy propagation more effective after PRE. + // x := rc(y) --> rc(x := y) + final CheckExpr rc = (CheckExpr) expr.expr(); + + if (replace) { + final 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, + + final int v = expr.valueNumber(); + final int lv = expr.target().valueNumber(); + final 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(); + } + + final 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(final 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(final 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(final 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(final RCExpr expr) { + boolean move = false; // Can we remove the RCExpr + + final 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(final ZeroCheckExpr expr) { + boolean move = false; + + final 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) { + final 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(final UCExpr expr) { + if (expr.expr() instanceof UCExpr) { + // Remove redundent update checks + + final 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(final ArithExpr expr) { + if (expr.left().type().isIntegral()) { + 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(final ArithExpr expr) { + final int v = expr.valueNumber(); + final int lv = expr.left().valueNumber(); + final 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; + + final int lval = ((Number) lexpr.value()).intValue(); + final 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... + final 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... + final 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(final ArithExpr expr) { + final int v = expr.valueNumber(); + final int lv = expr.left().valueNumber(); + final 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; + + final long lval = ((Long) lexpr.value()).longValue(); + final 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)) { + final 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)) { + final 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(final ArithExpr expr) { + final int v = expr.valueNumber(); + final int lv = expr.left().valueNumber(); + final 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; + } + + final Float rvalue = (Float) rexpr.value(); + final 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; + + final float lval = lvalue.floatValue(); + final 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(final ArithExpr expr) { + final int v = expr.valueNumber(); + final int lv = expr.left().valueNumber(); + final 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; + } + + final Double rvalue = (Double) rexpr.value(); + final 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; + + final double lval = lvalue.doubleValue(); + final 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(final CastExpr expr) { + // Note: we can't fold i2b, i2c, i2s, i2l, i2f, f2i, ... + // We only fold (String) "" and (C) null. + + final int v = expr.valueNumber(); + final 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); + final boolean effects = sideEffects.hasSideEffects(); + + if (effects) { + return; + } + } + + final 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().isReference()) { + if ((evalue == null) && expr.castType().isReference()) { + // 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(final NegExpr expr) { + final int v = expr.valueNumber(); + final 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. + + final 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 + + final NegExpr neg = (NegExpr) expr.expr(); + node = neg.expr(); + + if (replace) { + expr.parent().visit(new ReplaceVisitor(expr, node)); + expr.cleanupOnly(); + neg.cleanupOnly(); + } + } + } + + public void visitShiftExpr(final ShiftExpr expr) { + // Exploit shifting zero bits or shifting zero + + final int v = expr.valueNumber(); + final int ev = expr.expr().valueNumber(); + final 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) { + final 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; + } + + final 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) { + final 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; + } + + final 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(final IfZeroStmt stmt) { + // If the expression being compared to zero evaluates to a + // constant, then try to exploit this fact. + + final Block block = stmt.block(); + final FlowGraph cfg = block.graph(); + + final int v = stmt.valueNumber(); + final 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; + } + + final 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) { + final 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(final IfCmpStmt stmt) { + final Block block = stmt.block(); + final FlowGraph cfg = block.graph(); + + final int v = stmt.valueNumber(); + final int lv = stmt.left().valueNumber(); + final 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) { + final 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) { + final 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) { + final 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. + final int cmp = stmt.comparison(); + + if (replace) { + final 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. + final int cmp = stmt.comparison(); + + if (replace) { + final 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. + + final int cmp = stmt.comparison(); + + if (replace) { + final 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. + + final int lval = ((Integer) lvalue).intValue(); + final 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(final 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. + final Block block = stmt.block(); + final FlowGraph cfg = block.graph(); + + final int v = stmt.valueNumber(); + final 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; + + final Integer ivalue = (Integer) iexpr.value(); + + final 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(final PrintWriter pw) { + if (pw == null) { + return; + } + + final Iterator iter = values.iterator(); + + pw.println("Value Numbers mapped to constants\n"); + + for (int i = 0; iter.hasNext(); i++) { + final 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..1e18504 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/ValueFolding.java @@ -0,0 +1,290 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tree.*; + +/** + * 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 { + 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(final FlowGraph cfg) { + + final File dir = new File("ValueFoldingOutdir"); + if (ValueFolding.DUMP) { + // System.out.println(" Dumping Value Folding in directory: " + + // dir); + + // try { + // cfgFile = new File(dir, "cfg.txt"); + + // cfg.print(new PrintWriter(new FileWriter(cfgFile), true)); + + // dotFile = new File(dir, "cfg.dot"); + // cfg.printGraph(new PrintWriter(new FileWriter(dotFile), + // true)); + + // vnFile = new File(dir, "value.numbers"); + // vn = new PrintWriter(new FileWriter(vnFile), true); + + // dumpFile = new File(dir, "vn.dump"); + // dump = new PrintWriter(new FileWriter(dumpFile), true); + + // } catch (IOException ex) { + + // } + + vn = new PrintWriter(System.out, true); + dump = new PrintWriter(System.out, true); + } + + final EditorContext context = cfg.method().declaringClass().context(); + + sideEffects = new SideEffectChecker(context); + + folder = new ValueFolder(true, context); + + final SSAGraph ssaGraph = new SSAGraph(cfg); + + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(final List scc) { + // Maps Nodes in the SCC to their folded value + final HashMap map = new HashMap(scc.size() * 2 + 1); + + boolean changed = true; + + while (changed) { + changed = false; + + final Iterator iter = scc.iterator(); + + int x = 0; + while (iter.hasNext()) { + final Node node = (Node) iter.next(); + + if (ValueFolding.DUMP) { + x++; + dump.println("Folding SCC Node " + node + " (" + x + + " of " + scc.size() + ")"); + } + + if (fold(map, node)) { + changed = true; + } + } + + if (ValueFolding.DUMP) { + dump.println(""); + } + + if (scc.size() == 1) { + break; + } + } + } + }); + + cfg.removeUnreachable(); + + folder = null; + sideEffects = null; + + // Okay, we've successfully value folded, remove debugging files + if (ValueFolding.DUMP && !ValueFolding.SAVEDUMP) { + // cfgFile.delete(); + // dotFile.delete(); + // dumpFile.delete(); + // vnFile.delete(); + // dir.delete(); + } + } + + /** + * 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 sccNode + * A Node in the SCC of the SSA Graph + * + * @return True, if the value in the mapping was changed. + */ + boolean fold(final Map map, final Node sccNode) { + Node node = (Node) map.get(sccNode); + + if (ValueFolding.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 (ValueFolding.DEBUG) { + System.out.println("folding --- " + node + " in " + node.parent()); + } + + if (ValueFolding.DUMP) { + dump.println(" Folding " + node + " (" + "VN=" + + node.valueNumber() + ") in " + node.parent()); + } + + final 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); + final ConstantExpr oldValue = (ConstantExpr) folder.values.get(v); + ConstantExpr value = null; + + if (ValueFolding.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 (ValueFolding.DEBUG) { + System.out.println("changed " + oldValue + " to " + value); + } + + if (ValueFolding.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 (ValueFolding.DEBUG) { + System.out.println("folded " + node + " to " + folder.node); + } + + if (ValueFolding.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; + } +} 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..b90ab7c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/trans/ValueNumbering.java @@ -0,0 +1,489 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.trans; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.ssa.*; +import EDU.purdue.cs.bloat.tree.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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 { + public static boolean DEBUG = false; + + SSAGraph ssaGraph; + + HashMap 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(final FlowGraph cfg) { + // Specify directory into which all debugging files should be + // placed + + if (DUMP || ValueFolding.DUMP) { + final String className = cfg.method().declaringClass().type() + .className(); + final String methodName = cfg.method().name(); + + final 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 { + final File dumpFile = new File(debugDir, "vn_dump"); + dump = new PrintWriter(new FileWriter(dumpFile), true); + } catch (final IOException ex) { + System.err.println(ex.toString()); + } + } + + ssaGraph = new SSAGraph(cfg); + tuples = new HashMap(); + folder = new ValueFolder(false, cfg.method().declaringClass().context()); + next = 0; + + final HashMap valid = new HashMap(); + final HashMap optimistic = new HashMap(); + + ssaGraph.visitComponents(new ComponentVisitor() { + public void visitComponent(final List scc) { + if (ValueNumbering.DEBUG || DUMP) { + dump.println("\nNumbering SCC = " + scc); + } + + final Iterator e = scc.iterator(); + + while (e.hasNext()) { + final Node node = (Node) e.next(); + node.setValueNumber(-1); + } + + if (scc.size() > 1) { + if (ValueNumbering.DEBUG || DUMP) { + dump.println("Optimistic-----------------------"); + } + + boolean changed = true; + + while (changed) { + changed = false; + + final Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + final Node node = (Node) iter.next(); + + if (valnum(node, optimistic)) { + changed = true; + } + } + } + } + + if (ValueNumbering.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. + final Iterator iter = scc.iterator(); + + while (iter.hasNext()) { + final Node node = (Node) iter.next(); + valnum(node, valid); + } + } + }); + + if (ValueNumbering.DEBUG || DUMP) { + dump.println("Final value numbers--------------------------"); + printValueNumbers(cfg, new PrintWriter(dump)); + } + + if (DUMP) { + System.out.println(" Dumping to: " + debugDir); + + try { + final File valueNumbers = new File(debugDir, "scc.txt"); + ssaGraph + .printSCCs(new PrintWriter(new FileWriter(valueNumbers))); + } catch (final 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(final Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(final Node node) { + node.visitChildren(this); + Assert.isTrue(node.valueNumber() != -1, "No value number for " + + node); + } + }); + } + + private void printValueNumbers(final FlowGraph cfg, final PrintWriter pw) { + cfg.visit(new TreeVisitor() { + public void visitTree(final Tree tree) { + tree.visitChildren(this); + } + + public void visitNode(final 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(final Node node) { + if (ValueNumbering.DEBUG || DUMP) { + dump.println("folding " + node + " in " + node.parent()); + } + + final 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()) { + final 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 (ValueNumbering.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(final Node node, final HashMap 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 + final Node s = simplify(node); + + tuple = new Tuple(s); + tuples.put(node, tuple); + + if (ValueNumbering.DEBUG || DUMP) { + dump.println(" New tuple " + tuple); + } + + } else if (DUMP) { + dump.println(" " + node + " mapped to tuple " + tuple); + } + + final Node w = (Node) table.get(tuple); + + if (ValueNumbering.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 (ValueNumbering.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. + final Iterator iter = ssaGraph.equivalent(node).iterator(); + + while (iter.hasNext()) { + final Node v = (Node) iter.next(); + + final 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 (ValueNumbering.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(final Node node) { + this.node = node; + final List children = ssaGraph.children(node); + this.hash = NodeComparator.hashCode(node) + children.size(); + } + + public String toString() { + final List children = ssaGraph.children(node); + + String s = "<" + node + ", hash=" + hash; + + final Iterator iter = children.iterator(); + + while (iter.hasNext()) { + final Node child = (Node) iter.next(); + s += ", " + child + "{" + child.valueNumber() + "}"; + } + + s += ">"; + + return s; + } + + public int hashCode() { + return hash; + } + + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof Tuple) { + final 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; + } + + final List children1 = ssaGraph.children(node); + final 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 + final int[] used = new int[next]; + int free = 0; // The number of un-numbered children + + Iterator iter = children1.iterator(); + + while (iter.hasNext()) { + final Node child = (Node) iter.next(); + final int v = child.valueNumber(); + + if (v != -1) { + used[v]++; + + } else { + free++; + } + } + + iter = children2.iterator(); + + while (iter.hasNext()) { + final Node child = (Node) iter.next(); + final 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. + final Iterator iter1 = children1.iterator(); + final Iterator iter2 = children2.iterator(); + + while (iter1.hasNext() && iter2.hasNext()) { + final Node child1 = (Node) iter1.next(); + final Node child2 = (Node) iter2.next(); + + final int v1 = child1.valueNumber(); + final 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; + } + } +} 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/.cvsignore b/src/EDU/purdue/cs/bloat/tree/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..d1a4c50 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/AddressStoreStmt.java @@ -0,0 +1,63 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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 { + Subroutine sub; + + /** + * Constructor. + * + * @param sub + * + */ + public AddressStoreStmt(final Subroutine sub) { + this.sub = sub; + } + + public Subroutine sub() { + return sub; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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..c433be1 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ArithExpr.java @@ -0,0 +1,128 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * ArithExpr represents a binary arithmetic expression. It consists of two + * operands and an operator. + */ +public class ArithExpr extends Expr { + 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(final char operation, final Expr left, final Expr right, + final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + right.visit(visitor); + left.visit(visitor); + } else { + left.visit(visitor); + right.visit(visitor); + } + } + + public void visit(final 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(final 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..9270c5c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ArrayLengthExpr.java @@ -0,0 +1,74 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Expr array, final Type type) { + super(type); + this.array = array; + array.setParent(this); + } + + public Expr array() { + return array; + } + + public void visitForceChildren(final TreeVisitor visitor) { + if (visitor.reverse()) { + array.visit(visitor); + } else { + array.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitArrayLengthExpr(this); + } + + public int exprHashCode() { + return 3 + array.exprHashCode() ^ type.simple().hashCode(); + } + + public boolean equalsExpr(final 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..d6ea841 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ArrayRefExpr.java @@ -0,0 +1,98 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * ArrayRefExpr represents an expression that references an element in an array. + */ +public class ArrayRefExpr extends MemRefExpr { + Expr array; + + Expr index; + + 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(final Expr array, final Expr index, + final Type elementType, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + index.visit(visitor); + array.visit(visitor); + } else { + array.visit(visitor); + index.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitArrayRefExpr(this); + } + + public int exprHashCode() { + return 4 + array.exprHashCode() ^ index.exprHashCode(); + } + + public boolean equalsExpr(final 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..e071907 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/AscendVisitor.java @@ -0,0 +1,456 @@ +/* + * 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. + * + * 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 + */ + +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 { + + Hashtable defInfoMap; /* the same as the fields of Stack Optimizer */ + + Hashtable useInfoMap; /* of the same name */ + + LocalExpr start; /* where we start the search from */ + + Node previous; + + Vector visited; + + public AscendVisitor(final Hashtable defInfoMap, final Hashtable useInfoMap) { + this.defInfoMap = defInfoMap; + this.useInfoMap = useInfoMap; + + visited = new Vector(); + } + + abstract public void check(Node node); + + public void visitTree(final Tree tree) { + + final ListIterator iter = tree.stmts().listIterator( + tree.stmts().lastIndexOf(previous)); + + if (iter.hasPrevious()) { + final Stmt p = (Stmt) iter.previous(); + check(p); + } + /* + * Object prev = iter.previous(); if (prev instanceof LocalExpr) + * check(prev); + */ + } + + public void visitExprStmt(final ExprStmt stmt) { + + previous = stmt; + stmt.parent().visit(this); + } + + public void visitIfCmpStmt(final IfCmpStmt stmt) { + + if (stmt.right() == previous) { + check(stmt.left()); + } else if (stmt.left() == previous) { + previous = stmt; + stmt.parent().visit(this); + } + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + + previous = stmt; + stmt.parent.visit(this); + } + + public void visitInitStmt(final InitStmt stmt) { + + final 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(final GotoStmt stmt) { + + } + + public void visitLabelStmt(final LabelStmt stmt) { + + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + + previous = stmt; + stmt.parent().visit(this); + } + + public void visitPhiStmt(final 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(final CatchExpr expr) { + + } + + public void visitDefExpr(final DefExpr expr) { + if (expr instanceof MemExpr) { + visitMemExpr((MemExpr) expr); + } + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + + } + + public void visitRetStmt(final RetStmt stmt) { + + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + + previous = stmt; + stmt.parent.visit(this); + } + + public void visitReturnStmt(final ReturnStmt stmt) { + + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + + } + + public void visitStoreExpr(final 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(final JsrStmt stmt) { + + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + + if (previous == stmt.index()) { + previous = stmt; + stmt.parent.visit(this); + } + } + + public void visitThrowStmt(final ThrowStmt stmt) { + + } + + public void visitStmt(final Stmt stmt) { + + } + + public void visitSCStmt(final SCStmt stmt) { + + } + + public void visitSRStmt(final SRStmt stmt) { + + } + + public void visitArithExpr(final ArithExpr expr) { + + if (previous == expr.left()) { + previous = expr; + expr.parent.visit(this); + } else if (previous == expr.right()) { + check(expr.left()); + } + + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + + } + + public void visitMemExpr(final MemExpr expr) { + + if (expr instanceof MemRefExpr) { + visitMemRefExpr((MemRefExpr) expr); + } else if (expr instanceof VarExpr) { + visitVarExpr((VarExpr) expr); + } + + } + + public void visitMemRefExpr(final 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(final 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()) { + check(expr.array()); // right child + } + } + + public void visitCallExpr(final CallExpr expr) { + if (expr instanceof CallMethodExpr) { + visitCallMethodExpr((CallMethodExpr) expr); + } + if (expr instanceof CallStaticExpr) { + visitCallStaticExpr((CallStaticExpr) expr); + } + + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + + if (previous == expr.receiver()) { + previous = expr; + expr.parent.visit(this); + } + + else { + final 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(final CallStaticExpr expr) { + + final 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(final CastExpr expr) { + + previous = expr; + expr.parent.visit(this); + } + + public void visitConstantExpr(final ConstantExpr expr) { + + } + + public void visitFieldExpr(final FieldExpr expr) { + + if (previous == expr.object()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + + if (previous == expr.expr()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitLocalExpr(final LocalExpr expr) { + + } + + public void visitNegExpr(final NegExpr expr) { + + if (previous == expr.expr()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + + if (previous == expr.size()) { + previous = expr; + expr.parent.visit(this); + } + } + + public void visitNewExpr(final NewExpr expr) { + + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + + final 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(final 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(final ZeroCheckExpr expr) { + /* + * if (previous == expr.expr()) { previous = expr; + * expr.parent.visit(this); } + */ + } + + public void visitRCExpr(final RCExpr expr) { + + } + + public void visitUCExpr(final UCExpr expr) { + + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + + } + + public void visitShiftExpr(final 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()) { + check(expr.expr()); // the right child + } + } + + public void visitStackExpr(final StackExpr expr) { + + } + + public void visitVarExpr(final VarExpr expr) { + if (expr instanceof LocalExpr) { + visitLocalExpr((LocalExpr) expr); + } + if (expr instanceof StackExpr) { + visitStackExpr((StackExpr) expr); + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + + } + + public void visitExpr(final Expr expr) { + + } + + public void visitNode(final 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..b1d1af8 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Assign.java @@ -0,0 +1,37 @@ +/* + * 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. + * + * 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 + */ + +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..1a18795 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CallExpr.java @@ -0,0 +1,70 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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 MemberRef that + * represents the method itself. + * + * @see CallMethodExpr + * @see CallStaticExpr + */ +public abstract class CallExpr extends Expr { + Expr[] params; // The parameters to the method + + MemberRef 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(final Expr[] params, final MemberRef method, final Type type) { + super(type); + this.params = params; + this.method = method; + + for (int i = 0; i < params.length; i++) { + params[i].setParent(this); + } + } + + public MemberRef 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..be5b2c4 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CallMethodExpr.java @@ -0,0 +1,122 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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; + + 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(final int kind, final Expr receiver, + final Expr[] params, final MemberRef method, final Type type) { + super(params, method, type); + this.receiver = receiver; + this.kind = kind; + + receiver.setParent(this); + } + + public int kind() { + return kind; + } + + public Expr receiver() { + return receiver; + } + + public void visitForceChildren(final 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(final 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(final Expr other) { + return false; + } + + public Object clone() { + final 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, type)); + } +} 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..2fcbeb5 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CallStaticExpr.java @@ -0,0 +1,88 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Expr[] params, final MemberRef method, + final Type type) { + super(params, method, type); + } + + public void visitForceChildren(final 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(final 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(final Expr other) { + return false; + } + + public Object clone() { + final 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, type)); + } +} 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..7d2b2d0 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CastExpr.java @@ -0,0 +1,97 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * CastExpr represents an expression that casts an object to a given type. + */ +public class CastExpr extends Expr { + Expr expr; // An expression (object) to cast + + 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(final Expr expr, final 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(final Expr expr, final Type castType, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitCastExpr(this); + } + + public int exprHashCode() { + return 7 + expr.exprHashCode(); + } + + public boolean equalsExpr(final 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..d8b5614 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CatchExpr.java @@ -0,0 +1,83 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(MethodEditor) + * @see MethodEditor + */ +public class CatchExpr extends Expr { + Type catchType; + + /** + * Constructor. + * + * @param catchType + * The type of the exception that is being caught. + * @param type + * The type of this expression. + */ + public CatchExpr(final Type catchType, final Type type) { + super(type); + this.catchType = catchType; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitCatchExpr(this); + } + + public Type catchType() { + return catchType; + } + + public int exprHashCode() { + return 8 + type.simple().hashCode() ^ catchType.hashCode(); + } + + public boolean equalsExpr(final Expr other) { + if (other instanceof CatchExpr) { + final 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..c5bc9e6 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CheckExpr.java @@ -0,0 +1,75 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Expr expr, final Type type) { + super(type); + this.expr = expr; + expr.setParent(this); + } + + public void visitForceChildren(final 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() ^ type.simple().hashCode(); + } + + public boolean equalsExpr(final 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..a0aadab --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/CondExpr.java @@ -0,0 +1,41 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final 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..a8875bc --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ConstantExpr.java @@ -0,0 +1,101 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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 + + 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(final Object value, final Type type) { + super(type); + this.value = value; + } + + /** + * @return The operand of the ldc instruction + */ + public Object value() { + return value; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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(final 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..c3dae58 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/DefExpr.java @@ -0,0 +1,118 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Type type) { + super(type); + uses = new HashSet(); + version = DefExpr.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(); + + final List a = new ArrayList(uses); + + uses.clear(); + + final Iterator e = a.iterator(); + + while (e.hasNext()) { + final 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) { + final 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 HashSet(uses); + } + + public boolean hasUse(final Expr use) { + return uses.contains(use); + } + + protected void addUse(final Expr use) { + uses.add(use); + } + + protected void removeUse(final 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..10c2ded --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/DefInformation.java @@ -0,0 +1,41 @@ +/* + * 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. + * + * 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 + */ + +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(final 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..bd2448e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/DescendVisitor.java @@ -0,0 +1,445 @@ +/* + * 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. + * + * 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 + */ + +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 { + + Hashtable useInfoMap; + + Hashtable defInfoMap; + + boolean found; + + Node beginNode; // where this visitor starts its search from + + LocalExpr start; // where the original search began + + int exchangeFactor; + + public DescendVisitor(final Hashtable useInfoMap, final Hashtable defInfoMap) { + this.useInfoMap = useInfoMap; + this.defInfoMap = defInfoMap; + } + + public boolean search(final Node beginNode, final LocalExpr start) { + this.beginNode = beginNode; + this.start = start; + exchangeFactor = 0; + found = false; + + beginNode.visit(this); + + return found; + } + + public void visitExprStmt(final ExprStmt stmt) { + stmt.expr().visit(this); + } + + public void visitIfStmt(final IfStmt stmt) { + + if (stmt instanceof IfCmpStmt) { + visitIfCmpStmt((IfCmpStmt) stmt); + } else if (stmt instanceof IfZeroStmt) { + visitIfZeroStmt((IfZeroStmt) stmt); + } + } + + public void visitIfCmpStmt(final 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().isWide()) { + exchangeFactor++; // twice to get + } + // around wides + if (exchangeFactor < 3) { + stmt.right().visit(this); // search the right branch. + } + } + + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + + stmt.expr().visit(this); + + } + + public void visitInitStmt(final InitStmt stmt) { + + // would have been checked by the Type0Visitor + + } + + public void visitGotoStmt(final GotoStmt stmt) { + + } + + public void visitLabelStmt(final LabelStmt stmt) { + + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + + } + + public void visitPhiStmt(final 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(final CatchExpr expr) { + + } + + public void visitDefExpr(final DefExpr expr) { + if (expr instanceof MemExpr) { + visitMemExpr((MemExpr) expr); + } + + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + + } + + public void visitRetStmt(final RetStmt stmt) { + + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + + } + + public void visitReturnStmt(final ReturnStmt stmt) { + + } + + public void visitAddressStoreStmt(final 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(final StoreExpr expr) { + final 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) { + 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(final JsrStmt stmt) { + + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + + } + + public void visitThrowStmt(final ThrowStmt stmt) { + + } + + public void visitStmt(final Stmt stmt) { + + } + + public void visitSCStmt(final SCStmt stmt) { + + } + + public void visitSRStmt(final SRStmt stmt) { + + } + + public void visitArithExpr(final 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().isWide()) { + exchangeFactor++; // twice if wide + } + if (exchangeFactor < 3) { + expr.right().visit(this); // visit right branch + } + } + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + expr.array().visit(this); + } + + public void visitMemExpr(final MemExpr expr) { + if (expr instanceof LocalExpr) { + visitLocalExpr((LocalExpr) expr); + } + } + + public void visitMemRefExpr(final MemRefExpr expr) { + + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + + } + + public void visitCallExpr(final CallExpr expr) { + if (expr instanceof CallMethodExpr) { + visitCallMethodExpr((CallMethodExpr) expr); + } else if (expr instanceof CallStaticExpr) { + visitCallStaticExpr((CallStaticExpr) expr); + } + } + + public void visitCallMethodExpr(final 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); + final Expr[] params = expr.params(); + if (!found && (exchangeFactor < 2) && (params.length > 0)) { + exchangeFactor++; // (reciever won't be wide) + params[0].visit(this); + } + + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + + final 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(final CastExpr expr) { + expr.expr().visit(this); + } + + public void visitConstantExpr(final ConstantExpr expr) { + + } + + public void visitFieldExpr(final FieldExpr expr) { + expr.object.visit(this); + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + expr.expr().visit(this); + } + + /* needs to be different for Type0 and Type1 */ + public abstract void visitLocalExpr(LocalExpr expr); + + public void visitNegExpr(final NegExpr expr) { + expr.expr().visit(this); + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + expr.size().visit(this); + } + + public void visitNewExpr(final NewExpr expr) { + + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + // Think of the tree like + // (NewMultiArrayExpr) + // / \ + // count1 . + // / \ + // count2 etc. + // since multianewarray manipulates the stack like + // count1, [count1 ...] => ... + + final 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(final 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(final ZeroCheckExpr expr) { + // perhaps add something here + } + + public void visitRCExpr(final RCExpr expr) { + + } + + public void visitUCExpr(final UCExpr expr) { + + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + + } + + public void visitShiftExpr(final ShiftExpr expr) { + + } + + public void visitVarExpr(final VarExpr expr) { + if (expr instanceof LocalExpr) { + visitLocalExpr((LocalExpr) expr); + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + + } + + public void visitExpr(final 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..51c6504 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/EliminationInformation.java @@ -0,0 +1,46 @@ +/** + * All files in the distribution of BLOAT (Bytecode Level Optimization and + * Analysis tool for Java(tm)) are Copyright 1997-1998 by the Purdue + * Research Foundation of Purdue University. All rights reserved. + * + * 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 + */ + +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 = EliminationInformation.nextUN; + EliminationInformation.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..dbb7835 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Expr.java @@ -0,0 +1,216 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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(final 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(final Type type) { + + if (!this.type.equals(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(final 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); + + final 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(final Object obj) { + if (obj instanceof ExprComparator) { + final Expr other = ((ExprComparator) obj).expr; + return expr.equalsExpr(other) + && expr.type.simple().equals(other.type.simple()); + } + + 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..214f4ed --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ExprStmt.java @@ -0,0 +1,57 @@ +/* + * 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. + * + * 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 + */ + +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(final Expr expr) { + this.expr = expr; + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(final TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(final 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..4fd6c3b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/FieldExpr.java @@ -0,0 +1,89 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * FieldExpr represents the getfield opcode which fetches a field from + * an object. + * + * @see MemberRef + */ +public class FieldExpr extends MemRefExpr { + // getfield + + Expr object; // The object whose field we are fetching + + 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(final Expr object, final MemberRef field, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + object.visit(visitor); + } else { + object.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitFieldExpr(this); + } + + public int exprHashCode() { + return 11 + object.exprHashCode() ^ type.simple().hashCode(); + } + + public boolean equalsExpr(final 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..67b1a54 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/GotoStmt.java @@ -0,0 +1,59 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Block target) { + this.target = target; + } + + public void setTarget(final Block target) { + this.target = target; + } + + public Block target() { + return target; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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..f3d470e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/IfCmpStmt.java @@ -0,0 +1,83 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final int comparison, final Expr left, final Expr right, + final Block trueTarget, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + right.visit(visitor); + left.visit(visitor); + } else { + left.visit(visitor); + right.visit(visitor); + } + } + + public void visit(final 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..669f11f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/IfStmt.java @@ -0,0 +1,122 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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 falseTarget + * Basic Block that is executed when if statement is false. + */ + public IfStmt(final int comparison, final Block trueTarget, + final 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 = IfStmt.NE; + break; + case NE: + comparison = IfStmt.EQ; + break; + case LT: + comparison = IfStmt.GE; + break; + case GE: + comparison = IfStmt.LT; + break; + case GT: + comparison = IfStmt.LE; + break; + case LE: + comparison = IfStmt.GT; + break; + } + + final Block t = trueTarget; + trueTarget = falseTarget; + falseTarget = t; + } + + public void setTrueTarget(final Block target) { + this.trueTarget = target; + } + + public void setFalseTarget(final 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..44f1f90 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/IfZeroStmt.java @@ -0,0 +1,72 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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 falseTarget + * Basic Block that is executed if the expression evaluates to + * non-zero. + */ + public IfZeroStmt(final int comparison, final Expr expr, + final Block trueTarget, final 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(final TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(final 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..b55acdf --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/InitStmt.java @@ -0,0 +1,83 @@ +/* + * 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. + * + * 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 + */ + +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 { + LocalExpr[] targets; + + /** + * Constructor. + * + * @param targets + * The instances of LocalExpr that are to be initialized. + */ + public InitStmt(final 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(final TreeVisitor visitor) { + for (int i = 0; i < targets.length; i++) { + targets[i].visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitInitStmt(this); + } + + public Object clone() { + final 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..7098837 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/InstanceOfExpr.java @@ -0,0 +1,87 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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 + + 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(final Expr expr, final Type checkType, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitInstanceOfExpr(this); + } + + public int exprHashCode() { + return 12 + expr.exprHashCode(); + } + + public boolean equalsExpr(final 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..d7f5567 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/JsrStmt.java @@ -0,0 +1,75 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Subroutine sub, final Block follow) { + this.sub = sub; + this.follow = follow; + } + + public void setFollow(final Block follow) { + this.follow = follow; + } + + public Block follow() { + return follow; + } + + public Subroutine sub() { + return sub; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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..77e3408 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/JumpStmt.java @@ -0,0 +1,55 @@ +/* + * 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. + * + * 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 + */ + +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 HashSet(); + } + + /** + * 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(final 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..1ee714b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/LEGatherer.java @@ -0,0 +1,50 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Block b) { + + LEs = new Vector(); + + visitBlock(b); + + return LEs; + } + + public void visitLocalExpr(final 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..8b1ebff --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/LabelStmt.java @@ -0,0 +1,58 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * LabelStmt is a placeholder in a Tree for a Label (the target of a jump). + * + * @see Label + * @see Tree#addLabel + */ +public class LabelStmt extends Stmt { + Label label; + + /** + * Constructor. + * + * @param label + * The label that comprises this statement. + */ + public LabelStmt(final Label label) { + this.label = label; + } + + public Label label() { + return label; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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..78ab6ad --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/LeafExpr.java @@ -0,0 +1,32 @@ +/* + * 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. + * + * 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 + */ + +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..9e880e5 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/LocalExpr.java @@ -0,0 +1,100 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(Type) + */ +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(final int index, final boolean fromStack, final 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(final int index, final 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(Type.ADDRESS); + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitLocalExpr(this); + } + + /** + * @param other + * The other expression to compare against. + */ + public boolean equalsExpr(final Expr other) { + return (other instanceof LocalExpr) + && ((LocalExpr) other).type.simple().equals(type.simple()) + && (((LocalExpr) other).fromStack == fromStack) + && (((LocalExpr) other).index == index); + } + + public int exprHashCode() { + return 13 + (fromStack ? 0 : 1) + index + type.simple().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..e8aa28b --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Makefile @@ -0,0 +1,97 @@ +# 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. +# +# 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 + + +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\ + StackOptimizer.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..0f8d16c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/MemExpr.java @@ -0,0 +1,32 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * An expression that accesses a memory location. + */ +public abstract class MemExpr extends DefExpr { + public MemExpr(final 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..ff4710e --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/MemRefExpr.java @@ -0,0 +1,37 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final 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..a7938dd --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/MonitorStmt.java @@ -0,0 +1,72 @@ +/* + * 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. + * + * 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 + */ + +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; + + 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(final int kind, final Expr object) { + this.kind = kind; + this.object = object; + + object.setParent(this); + } + + public Expr object() { + return object; + } + + public int kind() { + return kind; + } + + public void visitForceChildren(final TreeVisitor visitor) { + object.visit(visitor); + } + + public void visit(final 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..0e57aa2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/NegExpr.java @@ -0,0 +1,73 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Expr expr, final Type type) { + super(type); + this.expr = expr; + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(final TreeVisitor visitor) { + if (visitor.reverse()) { + expr.visit(visitor); + } else { + expr.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitNegExpr(this); + } + + public int exprHashCode() { + return 14 + expr.exprHashCode(); + } + + public boolean equalsExpr(final 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..59c1b90 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/NewArrayExpr.java @@ -0,0 +1,85 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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; + + 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. + */ + public NewArrayExpr(final Expr size, final Type elementType, final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + size.visit(visitor); + } else { + size.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitNewArrayExpr(this); + } + + public int exprHashCode() { + return 15 + size.exprHashCode(); + } + + public boolean equalsExpr(final 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..f220286 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/NewExpr.java @@ -0,0 +1,71 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * NewExpr represents the new opcode that creates a new object of a + * specified type. + */ +public class NewExpr extends Expr { + // new + Type objectType; + + /** + * Constructor. + * + * @param objectType + * The type of the object to create. + * @param type + * The type of this expression. + */ + public NewExpr(final Type objectType, final Type type) { + super(type); + this.objectType = objectType; + } + + /** + * Returns the Type of the object being created. + */ + public Type objectType() { + return objectType; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitNewExpr(this); + } + + public int exprHashCode() { + return 16 + objectType.hashCode(); + } + + public boolean equalsExpr(final 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..2164f3f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/NewMultiArrayExpr.java @@ -0,0 +1,105 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * NewMultiArrayExpr represents the multianewarray opcode which + * creates a new multidimensional array. + */ +public class NewMultiArrayExpr extends Expr { + // multianewarray + + Expr[] dimensions; + + 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(final Expr[] dimensions, final Type elementType, + final 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(final 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(final 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(final Expr other) { + return false; + } + + public Object clone() { + final 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..607e736 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Node.java @@ -0,0 +1,269 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.io.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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(final 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(final 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(final TreeVisitor visitor) { + if (!visitor.prune()) { + visitForceChildren(visitor); + } + } + + public void visitOnly(final 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(final 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(final 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(final Node node) { + node.setParent(null); + node.cleanupOnly(); + node.visitChildren(this); + } + }); + } + + /** + * Replaces this node with another and perform cleanup. + */ + public void replaceWith(final 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(final Node node, final 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"); + + final 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); + + final Expr expr1 = (Expr) this; + final Expr expr2 = (Expr) node; + + // Make sure the expressions can be interchanged (i.e. their + // descriptors + // are compatible). + Assert.isTrue(expr1.type().simple().equals(expr2.type().simple()), + "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() { + final StringWriter w = new StringWriter(); + + visit(new PrintVisitor(w) { + protected void println(final 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..24f313d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/OperandStack.java @@ -0,0 +1,247 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Type type) { + final Expr top = (Expr) stack.remove(stack.size() - 1); + + final Type topType = top.type(); + + height -= topType.stackHeight(); + + if (type.isAddress()) { + if (!topType.isAddress()) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + } else if (type.isReference()) { + if (!topType.isReference()) { + throw new IllegalArgumentException("Expected " + type + + ", stack = " + toString()); + } + } else if (type.isIntegral()) { + if (!topType.isIntegral()) { + 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(final int index, final 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, final Expr expr) { + for (int i = stack.size() - 1; i >= 0; i--) { + final Expr top = (Expr) stack.get(i); + + if (depth == 0) { + stack.set(i, expr); + return; + } + + depth -= top.type().stackHeight(); + } + + 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) { + for (int i = stack.size() - 1; i >= 0; i--) { + final Expr top = (Expr) stack.get(i); + if (depth == 0) { + return top; + } + + depth -= top.type().stackHeight(); + } + + throw new IllegalArgumentException("Can't peek below stack bottom."); + } + + /** + * Pops a non-wide expression off the stack. + */ + public Expr pop1() { + final Expr top = (Expr) stack.remove(stack.size() - 1); + + final Type type = top.type(); + + if (type.isWide()) { + 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() { + final Expr top = (Expr) stack.remove(stack.size() - 1); + Expr[] a; + + final Type type = top.type(); + + if (type.isWide()) { + 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(final Expr expr) { + height += expr.type().stackHeight(); + 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(final int index) { + final 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..417091a --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/PhiCatchStmt.java @@ -0,0 +1,154 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.util.*; + +/** + * 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 { + ArrayList operands; + + /** + * Constructor. + * + * @param target + * Local variable to which the result of this phi-function is to + * be assigned. + */ + public PhiCatchStmt(final LocalExpr target) { + super(target); + this.operands = new ArrayList(); + } + + public void visitForceChildren(final TreeVisitor visitor) { + if (visitor.reverse()) { + target.visit(visitor); + } + + for (int i = 0; i < operands.size(); i++) { + final LocalExpr expr = (LocalExpr) operands.get(i); + expr.visit(visitor); + } + + if (!visitor.reverse()) { + target.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitPhiCatchStmt(this); + } + + /** + * Searches the list of operands for a local variable. + * + * @param def + * The local variable definition to search for. + * @return True, if def is found, otherwise, false. + */ + public boolean hasOperandDef(final LocalExpr def) { + for (int i = 0; i < operands.size(); i++) { + final 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(final LocalExpr operand) { + for (int i = 0; i < operands.size(); i++) { + final 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++) { + final LocalExpr ei = (LocalExpr) operands.get(i); + + for (int j = operands.size() - 1; j > i; j--) { + final 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(final int i, final Expr expr) { + final 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(final 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..62a985d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/PhiJoinStmt.java @@ -0,0 +1,152 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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 { + Map operands; // Operands to a PhiStmt (mapping between a Block + + // and a VarExpr occurring at that block) + 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(final VarExpr target, final Block block) { + super(target); + + this.block = block; + this.operands = new HashMap(); + + final Iterator preds = block.graph().preds(block).iterator(); + + while (preds.hasNext()) { + final Block pred = (Block) preds.next(); + final 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(final Block block, final Expr expr) { + final 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(final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + target.visit(visitor); + } + + final Iterator e = operands().iterator(); + + while (e.hasNext()) { + final Expr operand = (Expr) e.next(); + operand.visit(visitor); + } + + if (!visitor.reverse()) { + target.visit(visitor); + } + } + + public void visit(final 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..0154e46 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/PhiStmt.java @@ -0,0 +1,64 @@ +/* + * 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. + * + * 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 + */ + +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(final 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..3e195bc --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/PrintVisitor.java @@ -0,0 +1,737 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.io.*; +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Writer out) { + this.out = new PrintWriter(out); + } + + public PrintVisitor(final PrintStream out) { + this.out = new PrintWriter(out); + } + + protected void println() { + out.println(); + } + + protected void println(final Object s) { + out.println(s); + } + + protected void print(final Object s) { + out.print(s); + } + + public void visitFlowGraph(final FlowGraph cfg) { + cfg.source().visit(this); + + final Iterator e = cfg.trace().iterator(); + + while (e.hasNext()) { + final Block block = (Block) e.next(); + block.visit(this); + } + + cfg.sink().visit(this); + + this.out.flush(); + } + + public void visitBlock(final Block block) { + println(); + println(block); + + final 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(final ExprStmt stmt) { + print("eval "); + stmt.expr().visit(this); + println(); + } + + public void visitIfZeroStmt(final 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().isReference()) { + print(" null"); + } else { + print(" 0"); + } + + print(") then " + stmt.trueTarget() + " else " + stmt.falseTarget()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitIfCmpStmt(final 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(final InitStmt stmt) { + print("INIT"); + + final 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(final GotoStmt stmt) { + print("goto " + stmt.target().label()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitLabelStmt(final LabelStmt stmt) { + if (stmt.label() != null) { + println(stmt.label()); + } + } + + public void visitMonitorStmt(final 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(final CatchExpr expr) { + print("Catch(" + expr.catchType() + ")"); + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + print("("); + + final 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(", "); + } + } + } + + final String[] str = new String[] { "swap", "dup", "dup_x1", "dup_x2", + "dup2", "dup2_x1", "dup2_x2" }; + + print(") := " + str[stmt.kind()] + "("); + + final 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(final PhiJoinStmt stmt) { + if (stmt.target() != null) { + stmt.target().visit(this); + } + + print(" := Phi("); + + if (stmt.hasParent()) { + final Tree tree = (Tree) stmt.parent(); + final Block block = tree.block(); + + final Iterator e = block.graph().preds(block).iterator(); + + while (e.hasNext()) { + final Block pred = (Block) e.next(); + + final Expr operand = stmt.operandAt(pred); + print(pred.label() + "="); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + } else { + final Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + final Expr operand = (Expr) e.next(); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + } + + println(")"); + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + if (stmt.target() != null) { + stmt.target().visit(this); + } + + print(" := Phi-Catch("); + + final Iterator e = stmt.operands().iterator(); + + while (e.hasNext()) { + final Expr operand = (Expr) e.next(); + operand.visit(this); + + if (e.hasNext()) { + print(", "); + } + } + + println(")"); + } + + public void visitRetStmt(final RetStmt stmt) { + print("ret from " + stmt.sub()); + println(" caught by " + stmt.catchTargets()); + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + print("return "); + + if (stmt.expr() != null) { + stmt.expr().visit(this); + } + + println(" caught by " + stmt.catchTargets()); + } + + public void visitReturnStmt(final ReturnStmt stmt) { + print("return"); + println(" caught by " + stmt.catchTargets()); + } + + public void visitStoreExpr(final StoreExpr expr) { + print("("); + + if (expr.target() != null) { + expr.target().visit(this); + } + + print(" := "); + + if (expr.expr() != null) { + expr.expr().visit(this); + } + + print(")"); + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + print("La"); + + if (stmt.sub() != null) { + print(new Integer(stmt.sub().returnAddress().index())); + } else { + print("???"); + } + + println(" := returnAddress"); + } + + public void visitJsrStmt(final 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(final 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(final ThrowStmt stmt) { + print("throw "); + + if (stmt.expr() != null) { + stmt.expr().visit(this); + } + + println(" caught by " + stmt.catchTargets()); + } + + public void visitSCStmt(final SCStmt stmt) { + print("aswizzle "); + if (stmt.array() != null) { + stmt.array().visit(this); + } + if (stmt.index() != null) { + stmt.index().visit(this); + } + } + + public void visitSRStmt(final 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(final 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(final ArrayLengthExpr expr) { + if (expr.array() != null) { + expr.array().visit(this); + } + print(".length"); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + if (expr.array() != null) { + expr.array().visit(this); + } + print("["); + if (expr.index() != null) { + expr.index().visit(this); + } + print("]"); + } + + public void visitCallMethodExpr(final 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(final 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(final CastExpr expr) { + print("((" + expr.castType() + ") "); + if (expr.expr() != null) { + expr.expr().visit(this); + } + print(")"); + } + + public void visitConstantExpr(final ConstantExpr expr) { + if (expr.value() instanceof String) { + final StringBuffer sb = new StringBuffer(); + + final String s = (String) expr.value(); + + for (int i = 0; i < s.length(); i++) { + final 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(final FieldExpr expr) { + if (expr.object() != null) { + expr.object().visit(this); + } + print("."); + if (expr.field() != null) { + print(expr.field().nameAndType().name()); + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + if (expr.expr() != null) { + expr.expr().visit(this); + } + print(" instanceof " + expr.checkType()); + } + + public void visitLocalExpr(final LocalExpr expr) { + if (expr.fromStack()) { + print("T"); + } else { + print("L"); + } + + print(expr.type().shortName().toLowerCase()); + print(Integer.toString(expr.index())); + + final DefExpr def = expr.def(); + + if ((def == null) || (def.version() == -1)) { + print("_undef"); + + } else { + print("_" + def.version()); + } + } + + public void visitNegExpr(final NegExpr expr) { + print("-"); + if (expr.expr() != null) { + expr.expr().visit(this); + } + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + print("new " + expr.elementType() + "["); + if (expr.size() != null) { + expr.size().visit(this); + } + print("]"); + } + + public void visitNewExpr(final NewExpr expr) { + print("new " + expr.objectType()); + } + + public void visitNewMultiArrayExpr(final 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(final ZeroCheckExpr expr) { + if (expr.expr().type().isReference()) { + print("notNull("); + } else { + print("notZero("); + } + + if (expr.expr() != null) { + expr.expr().visit(this); + } + + print(")"); + } + + public void visitRCExpr(final RCExpr expr) { + print("rc("); + if (expr.expr() != null) { + expr.expr().visit(this); + } + print(")"); + } + + public void visitUCExpr(final UCExpr expr) { + if (expr.kind() == UCExpr.POINTER) { + print("aupdate("); + } else { + print("supdate("); + } + + if (expr.expr() != null) { + expr.expr().visit(this); + } + print(")"); + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + print("returnAddress"); + } + + public void visitShiftExpr(final 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(final StackExpr expr) { + print("S" + expr.type().shortName().toLowerCase() + expr.index()); + + final DefExpr def = expr.def(); + + if ((def == null) || (def.version() == -1)) { + print("_undef"); + } else { + print("_" + def.version()); + } + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + if (expr.field() != null) { + print(expr.field().declaringClass() + "." + + expr.field().nameAndType().name()); + } + } + + public void visitExpr(final Expr expr) { + print("EXPR"); + } + + public void visitStmt(final 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..4f33f7a --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/RCExpr.java @@ -0,0 +1,52 @@ +/* + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Expr expr, final Type type) { + super(expr, type); + } + + public void visit(final TreeVisitor visitor) { + visitor.visitRCExpr(this); + } + + public boolean equalsExpr(final 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..b674a63 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ReplaceVisitor.java @@ -0,0 +1,460 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Node from, final 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(final 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. + final ListIterator iter = tree.stmts + .listIterator(tree.stmts.size()); + + while (iter.hasPrevious()) { + final Stmt s = (Stmt) iter.previous(); + if (s == from) { + iter.set(to); + break; + } + } + } else { + tree.visitChildren(this); + } + } + + public void visitExprStmt(final ExprStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitInitStmt(final 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(final GotoStmt stmt) { + stmt.visitChildren(this); + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + if (stmt.object == from) { + stmt.object = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitStackManipStmt(final 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(final CatchExpr expr) { + expr.visitChildren(this); + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + if (stmt.target == from) { + stmt.target = (VarExpr) to; + ((VarExpr) to).setParent(stmt); + } else { + final Iterator e = stmt.operands.keySet().iterator(); + + while (e.hasNext()) { + final 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(final PhiCatchStmt stmt) { + if (stmt.target == from) { + stmt.target = (LocalExpr) to; + ((LocalExpr) to).setParent(stmt); + } else { + final ListIterator e = stmt.operands.listIterator(); + + while (e.hasNext()) { + final LocalExpr expr = (LocalExpr) e.next(); + + if (expr == from) { + e.set(to); + from.cleanup(); + ((LocalExpr) to).setParent(stmt); + return; + } + } + + stmt.visitChildren(this); + } + } + + public void visitRetStmt(final RetStmt stmt) { + stmt.visitChildren(this); + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitReturnStmt(final ReturnStmt stmt) { + stmt.visitChildren(this); + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + stmt.visitChildren(this); + } + + public void visitStoreExpr(final 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(final SwitchStmt stmt) { + if (stmt.index == from) { + stmt.index = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitThrowStmt(final ThrowStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitSCStmt(final 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(final 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(final DefExpr expr) { + expr.visitChildren(this); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + if (expr.array == from) { + expr.array = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitArithExpr(final 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(final 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(final 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(final 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(final CastExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitConstantExpr(final ConstantExpr expr) { + expr.visitChildren(this); + } + + public void visitFieldExpr(final FieldExpr expr) { + if (expr.object == from) { + expr.object = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitLocalExpr(final LocalExpr expr) { + expr.visitChildren(this); + } + + public void visitNegExpr(final NegExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + if (expr.size == from) { + expr.size = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitNewExpr(final NewExpr expr) { + expr.visitChildren(this); + } + + public void visitNewMultiArrayExpr(final 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(final IfZeroStmt stmt) { + if (stmt.expr == from) { + stmt.expr = (Expr) to; + ((Expr) to).setParent(stmt); + } else { + stmt.visitChildren(this); + } + } + + public void visitIfCmpStmt(final 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(final ReturnAddressExpr expr) { + expr.visitChildren(this); + } + + public void visitShiftExpr(final 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(final ZeroCheckExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitRCExpr(final RCExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitUCExpr(final UCExpr expr) { + if (expr.expr == from) { + expr.expr = (Expr) to; + ((Expr) to).setParent(expr); + } else { + expr.visitChildren(this); + } + } + + public void visitStackExpr(final StackExpr expr) { + expr.visitChildren(this); + } + + public void visitStaticFieldExpr(final 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..3ec1e65 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/RetStmt.java @@ -0,0 +1,63 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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 { + // ret + + 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(final Subroutine sub) { + this.sub = sub; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final 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..0ee7bac --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ReturnAddressExpr.java @@ -0,0 +1,58 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final Type type) { + super(type); + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitReturnAddressExpr(this); + } + + public int exprHashCode() { + return 18; + } + + public boolean equalsExpr(final 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..1b4032d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ReturnExprStmt.java @@ -0,0 +1,59 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +/** + * ReturnExprStmt represents the areturn opcode which returns a + * reference from a method. + */ +public class ReturnExprStmt extends JumpStmt { + // areturn + + Expr expr; + + /** + * Constructor. + * + * @param expr + * The expression (reference) returned by this return statement. + */ + public ReturnExprStmt(final Expr expr) { + this.expr = expr; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(final TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(final 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..6e1bb8f --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ReturnStmt.java @@ -0,0 +1,46 @@ +/** + * 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. + * + * 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 + */ + +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(final TreeVisitor visitor) { + } + + public void visit(final 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..f932e97 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/SCStmt.java @@ -0,0 +1,87 @@ +/** + * 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. + * + * 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 + */ + +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(final Expr a, final 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(final boolean val) { + this.redundant = val; + } + + public void visit(final TreeVisitor visitor) { + visitor.visitSCStmt(this); + } + + public Object clone() { + return copyInto(new SCStmt((Expr) array.clone(), (Expr) index.clone())); + } + + public void visitForceChildren(final 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..32b9dd7 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/SRStmt.java @@ -0,0 +1,84 @@ +/** + * 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. + * + * 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 + */ + +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(final Expr a, final Expr s, final 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(final TreeVisitor visitor) { + visitor.visitSRStmt(this); + } + + public Object clone() { + return copyInto(new SRStmt((Expr) array.clone(), (Expr) start.clone(), + (Expr) end.clone())); + } + + public void visitForceChildren(final 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..42e774c --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ShiftExpr.java @@ -0,0 +1,106 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * ShiftExpr represents a bit shift operation. + */ +public class ShiftExpr extends Expr { + 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(final int dir, final Expr expr, final Expr bits, + final 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(final TreeVisitor visitor) { + if (visitor.reverse()) { + bits.visit(visitor); + expr.visit(visitor); + } else { + expr.visit(visitor); + bits.visit(visitor); + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitShiftExpr(this); + } + + public int exprHashCode() { + return 19 + dir ^ expr.exprHashCode() ^ bits.exprHashCode(); + } + + public boolean equalsExpr(final 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..a1c7ccd --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/StackExpr.java @@ -0,0 +1,62 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final int index, final Type type) { + super(index, type); + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitStackExpr(this); + } + + public int exprHashCode() { + return 20 + index + type.simple().hashCode(); + } + + public boolean equalsExpr(final Expr other) { + return (other instanceof StackExpr) + && ((StackExpr) other).type.simple().equals(type.simple()) + && (((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..ddeaeea --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/StackManipStmt.java @@ -0,0 +1,138 @@ +/** + * 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. + * + * 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 + */ + +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(final StackExpr[] target, final StackExpr[] source, + final 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(final TreeVisitor visitor) { + visitor.visitStackManipStmt(this); + } + + public void visitForceChildren(final 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() { + final StackExpr[] t = new StackExpr[target.length]; + + for (int i = 0; i < target.length; i++) { + t[i] = (StackExpr) target[i].clone(); + } + + final 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/StackOptimizer.java b/src/EDU/purdue/cs/bloat/tree/StackOptimizer.java new file mode 100644 index 0000000..9b3ab10 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/StackOptimizer.java @@ -0,0 +1,350 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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 { + + static boolean DEBUG = false; + + Hashtable defInfoMap; /* + * maps LocalExprs (which are definitions) to + * DefInformations + */ + + Hashtable useInfoMap; /* maps LocalExprs to UseInformations */ + + Block owningBlock; + + public StackOptimizer(final Block owningBlock) { + this.owningBlock = owningBlock; + defInfoMap = new Hashtable(); + useInfoMap = new Hashtable(); + } + + public static void optimizeCFG(final FlowGraph cfg) { + + final List blocks = cfg.preOrder(); + for (final Iterator it = blocks.iterator(); it.hasNext();) { + ((Block) it.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() { + + final 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()) { + final 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) { + + final 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) && ((ArithExpr) current + .parent()).left().type().isIntegral()) || ((((ArithExpr) current + .parent()).right() instanceof ConstantExpr) && ((ArithExpr) current + .parent()).left().type().isIntegral())) + && (((StoreExpr) current.parent().parent()).target() instanceof LocalExpr) + && (((LocalExpr) ((StoreExpr) current.parent().parent()) + .target()).index() == current.index())) { + DI.type1s += 3; + } 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().isWide()) { + 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(final 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; + } + + final DefInformation DI = (DefInformation) defInfoMap.get(expr); + if (DI == null) { + if (StackOptimizer.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(final LocalExpr expr) { + + int toReturn = 0; + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (StackOptimizer.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(final LocalExpr expr) { + + int toReturn = 0; + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (StackOptimizer.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(final LocalExpr expr) { + + int toReturn = 0; + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (StackOptimizer.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(final LocalExpr expr) { + + if (expr.isDef()) { + return false; + } + + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (StackOptimizer.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(final LocalExpr expr) { + + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + + if (UI == null) { + if (StackOptimizer.DEBUG) { + System.err + .println("Error in StackOptimizer.onStack: parameter not found in useInfoMap"); + } + return false; + } + + return (onStack(expr) && (UI.type == 1)); + } + + public void infoDisplay(final LocalExpr expr) { + + final UseInformation UI = (UseInformation) useInfoMap.get(expr); + final 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/StaticFieldExpr.java b/src/EDU/purdue/cs/bloat/tree/StaticFieldExpr.java new file mode 100644 index 0000000..686d995 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/StaticFieldExpr.java @@ -0,0 +1,70 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * StaticFieldExpr represents the getstatic opcode which gets a + * static (class) field from a class. + */ +public class StaticFieldExpr extends MemRefExpr { + // getstatic + + MemberRef field; + + /** + * Constructor. + * + * @param field + * The field to access. + * @param type + * The type of this expression. + */ + public StaticFieldExpr(final MemberRef field, final Type type) { + super(type); + this.field = field; + } + + public MemberRef field() { + return field; + } + + public void visitForceChildren(final TreeVisitor visitor) { + } + + public void visit(final TreeVisitor visitor) { + visitor.visitStaticFieldExpr(this); + } + + public int exprHashCode() { + return 21 + field.hashCode() ^ type.simple().hashCode(); + } + + public boolean equalsExpr(final 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..c473902 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Stmt.java @@ -0,0 +1,37 @@ +/** + * 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. + * + * 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 + */ + +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..d8223a6 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/StoreExpr.java @@ -0,0 +1,108 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final MemExpr target, final Expr expr, final 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(final 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(final TreeVisitor visitor) { + visitor.visitStoreExpr(this); + } + + public int exprHashCode() { + return 22 + target.exprHashCode() ^ expr.exprHashCode(); + } + + public boolean equalsExpr(final 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..79203b9 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/SwitchStmt.java @@ -0,0 +1,97 @@ +/** + * 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. + * + * 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 + */ +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * SwitchStmt represents a switch statement. + */ +public class SwitchStmt extends JumpStmt { + Expr index; + + Block defaultTarget; + + Block[] targets; + + 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(final Expr index, final Block defaultTarget, + final Block[] targets, final 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(final Block block) { + this.defaultTarget = block; + } + + public Block defaultTarget() { + return defaultTarget; + } + + public Block[] targets() { + return targets; + } + + public int[] values() { + return values; + } + + public void visitForceChildren(final TreeVisitor visitor) { + index.visit(visitor); + } + + public void visit(final TreeVisitor visitor) { + visitor.visitSwitchStmt(this); + } + + public Object clone() { + final Block[] t = new Block[targets.length]; + System.arraycopy(targets, 0, t, 0, targets.length); + + final 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..aa6cfd7 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Swizzler.java @@ -0,0 +1,123 @@ +/** + * 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. + * + * 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 + */ +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(final Expr var, final Expr tgt, final Expr val, + final 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(final Expr end) { + this.end_val = end; + } + + /** + * @param a + * The array that is indexed by the induction variable. + */ + public void set_array(final Expr a) { + this.array = a; + } + + /** + * @param sc + * The aswizzle statement that could be removed from the block. + */ + public void set_aswizzle(final 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..82a0782 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ThrowStmt.java @@ -0,0 +1,58 @@ +/** + * 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. + * + * 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 + */ +package EDU.purdue.cs.bloat.tree; + +/** + * ThrowStmt represents the athrow opcode which throws an exception + * or error. + */ +public class ThrowStmt extends JumpStmt { + // athrow + + Expr expr; + + /** + * Constructor. + * + * @param expr + * The exception to be thrown. + */ + public ThrowStmt(final Expr expr) { + this.expr = expr; + + expr.setParent(this); + } + + public Expr expr() { + return expr; + } + + public void visitForceChildren(final TreeVisitor visitor) { + expr.visit(visitor); + } + + public void visit(final 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..677c6d0 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Tree.java @@ -0,0 +1,3058 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import java.util.*; + +import EDU.purdue.cs.bloat.cfg.*; +import EDU.purdue.cs.bloat.editor.*; +import EDU.purdue.cs.bloat.reflect.*; +import EDU.purdue.cs.bloat.util.*; + +/** + * 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, Opcode { + public static boolean DEBUG = false; + + public static boolean FLATTEN = false; + + public static boolean USE_STACK = true; // 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(final String s) { + if (Tree.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(final Block block, final 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++) { + final Expr expr = predStack.get(i); + final 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(final Collection locals) { + final LocalExpr[] t = new LocalExpr[locals.size()]; + + if (t.length == 0) { + return; + } + + final 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(final Stmt stmt) { + stmts.remove(stmt); + } + + /** + * Removes the last non-Label statement from the statement list. + */ + public void removeLastStmt() { + final ListIterator iter = stmts.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + final 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 { + /** + * Clear the contents of this statement list. + */ + public void clear() { + final 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(final 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(final Collection c) { + boolean changed = false; + + if (c == this) { + changed = size() > 0; + clear(); + } else { + final 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(final Collection c) { + boolean changed = false; + + if (c == this) { + return false; + } + + final 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(final int index, final Object element) { + if (index < size()) { + final 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(final int index) { + final 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(final 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(final Object obj) { + Assert.isTrue(obj instanceof Stmt); + ((Stmt) obj).setParent(Tree.this); + last = null; + iter.add(obj); + } + + public void set(final 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() { + final ListIterator iter = stmts.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + final Stmt s = (Stmt) iter.previous(); + + 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(final Stmt stmt, final Stmt after) { + if (Tree.DEBUG) { + System.out.println("insert: " + stmt + " after " + after); + } + + final ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + final 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(final Stmt stmt, final Stmt before) { + if (Tree.DEBUG) { + System.out.println("insert: " + stmt + " before " + before); + } + + final ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + final 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(final Stmt stmt) { + if (Tree.DEBUG) { + System.out.println("prepend: " + stmt + " in " + block); + } + + final ListIterator iter = stmts.listIterator(); + + while (iter.hasNext()) { + final 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++) { + final Expr expr = stack.get(i); + + if (Tree.USE_STACK) { + // Save to a stack variable only if we'll create a new + // variable there. + if (!(expr instanceof StackExpr) + || (((StackExpr) expr).index() != height)) { + + final StackExpr target = new StackExpr(height, expr.type()); + + // Make a new statement to store the expression that was + // part of the stack into memory. + final Stmt store = new ExprStmt(new StoreExpr(target, expr, + expr.type())); + + appendStmt(store); + + final StackExpr copy = (StackExpr) target.clone(); + copy.setDef(null); + stack.set(i, copy); + } + + } else { + if (!(expr instanceof LocalExpr) + || !((LocalExpr) expr).fromStack() + || (((LocalExpr) expr).index() != height)) { + + final LocalExpr target = newStackLocal(nextIndex++, expr + .type()); + + final Stmt store = new ExprStmt(new StoreExpr(target, expr, + expr.type())); + + appendStmt(store); + + final LocalExpr copy = (LocalExpr) target.clone(); + copy.setDef(null); + stack.set(i, copy); + } + } + + height += expr.type().stackHeight(); + } + } + + /** + * Add a statement to this Tree statement list and specify that this is the + * statement's parent. + */ + private void appendStmt(final 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(final 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(final Stmt stmt) { + final 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(final String msg) { + final MethodEditor method = block.graph().method(); + + throw new ClassFormatException("Method " + + method.declaringClass().type().className() + "." + + method.name() + " " + method.type() + ": " + 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 + */ + public void addInstruction(final Instruction inst, final Block next) { + Assert.isTrue(inst.isJsr() || inst.isConditionalJump(), + "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. + */ + public void addInstruction(final Instruction inst) { + Assert.isTrue(!inst.isJsr() && !inst.isConditionalJump(), + "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 + */ + public void addInstruction(final Instruction inst, final Subroutine sub) { + Assert.isTrue(inst.isRet() + || (inst.opcodeClass() == Opcode.opcx_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. + */ + public void addLabel(final Label label) { + // Add before a dup, but after any other instruction. + if (last != null) { + switch (last.opcodeClass()) { + case opcx_dup: + case opcx_dup2: + case opcx_dup_x1: + case opcx_dup2_x1: + case opcx_dup_x2: + case opcx_dup2_x2: + break; + default: + addInst(last, false); + last = null; + break; + } + } + + 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. + */ + private void addInst(final Instruction inst, final boolean saveValue) { + if (Tree.DEBUG) { + // Print the contents of the stack + for (int i = 0; i < stack.size(); i++) { + final Expr exp = stack.peek(i); + System.out.println((i > 0 ? "-" + i : " " + i) + ": " + exp); + } + } + + if (Tree.DEBUG) { + System.out.println(" add " + inst + " save=" + saveValue); + } + + try { + this.saveValue = saveValue; + + if (Tree.FLATTEN) { + saveStack(); + } + + inst.visit(this); + + } catch (final 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(final Instruction inst) { + // Build the tree, trying to convert dup-stores + + if (last == null) { + last = inst; + + } else { + switch (last.opcodeClass()) { + case opcx_dup: + switch (inst.opcodeClass()) { + case opcx_astore: + case opcx_fstore: + case opcx_istore: + case opcx_putstatic: + addInst(inst, true); + last = null; + break; + } + break; + case opcx_dup2: + switch (inst.opcodeClass()) { + case opcx_dstore: + case opcx_lstore: + case opcx_putstatic: + addInst(inst, true); + last = null; + break; + } + break; + case opcx_dup_x1: + switch (inst.opcodeClass()) { + case opcx_putfield: + case opcx_putfield_nowb: + addInst(inst, true); + last = null; + break; + } + break; + case opcx_dup2_x1: + switch (inst.opcodeClass()) { + case opcx_putfield: + case opcx_putfield_nowb: + addInst(inst, true); + last = null; + break; + } + break; + case opcx_dup_x2: + switch (inst.opcodeClass()) { + case opcx_aastore: + case opcx_bastore: + case opcx_castore: + case opcx_fastore: + case opcx_iastore: + case opcx_sastore: + addInst(inst, true); + last = null; + break; + } + break; + case opcx_dup2_x2: + switch (inst.opcodeClass()) { + case opcx_dastore: + case opcx_lastore: + addInst(inst, true); + last = null; + break; + } + break; + } + + 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.isJump() || inst.isSwitch() || inst.isThrow() + || inst.isReturn() || inst.isJsr() || inst.isRet()) { + addInst(inst, false); + last = null; + } + } + + /** + * Returns a new StackExpr for the top of the operand stack. + */ + public StackExpr newStack(final Type type) { + return new StackExpr(Tree.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(final int index, final 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(final int index, final 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(final Type type) { + final LocalVariable var = block.graph().method().newLocal(type); + return new LocalExpr(var.index(), type); + } + + /** + * Returns a String representation of this Tree. + */ + public String toString() { + String x = "(TREE " + block + " stack="; + + for (int i = 0; i < stack.size(); i++) { + final Expr expr = stack.get(i); + x += expr.type().shortName(); + } + + return x + ")"; + } + + /** + * Adds no statements to the statement list. + */ + public void visit_nop(final Instruction inst) { + } + + /** + * Pushes a ConstantExpr onto the operand stack. + * + * @see ConstantExpr + */ + public void visit_ldc(final Instruction inst) { + final Object value = inst.operand(); + Type type; + + if (value == null) { + type = Type.NULL; + } else if (value instanceof Integer) { + type = Type.INTEGER; + } 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; + } + // FIXME this won't work - check usages + else if (value instanceof Type) { + type = Type.CLASS; + } else { + throwClassFormatException("Illegal constant type: " + + value.getClass().getName() + ": " + value); + return; + } + + final Expr top = new ConstantExpr(value, type); + stack.push(top); + } + + /** + * All visit_xload push a LocalExpr onto the operand + * stack. + * + * @see LocalExpr + */ + public void visit_iload(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr top = new LocalExpr(operand.index(), Type.INTEGER); + stack.push(top); + } + + public void visit_lload(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr top = new LocalExpr(operand.index(), Type.LONG); + stack.push(top); + } + + public void visit_fload(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr top = new LocalExpr(operand.index(), Type.FLOAT); + stack.push(top); + } + + public void visit_dload(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr top = new LocalExpr(operand.index(), Type.DOUBLE); + stack.push(top); + } + + public void visit_aload(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr top = new LocalExpr(operand.index(), Type.OBJECT); + stack.push(top); + + db(" aload: " + top); + } + + /** + * All visit_xaload push an ArrayRefExpr onto the + * operand stack. + */ + public void visit_iaload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.INTEGER.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.INTEGER, + Type.INTEGER); + stack.push(top); + } + + public void visit_laload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.LONG.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visit_faload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.FLOAT.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visit_daload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.DOUBLE.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.DOUBLE, + Type.DOUBLE); + stack.push(top); + } + + public void visit_aaload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.OBJECT.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.OBJECT, + Type.OBJECT); + stack.push(top); + } + + public void visit_baload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.BYTE.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.BYTE, Type.BYTE); + stack.push(top); + } + + public void visit_caload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.CHARACTER.arrayType()); + final Expr top = new ArrayRefExpr(array, index, Type.CHARACTER, + Type.CHARACTER); + stack.push(top); + } + + public void visit_saload(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.SHORT.arrayType()); + final 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(final MemExpr target, final Expr expr) { + 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 visit_istore(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr expr = stack.pop(Type.INTEGER); + final LocalExpr target = new LocalExpr(operand.index(), expr.type()); + addStore(target, expr); + } + + public void visit_lstore(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr expr = stack.pop(Type.LONG); + final LocalExpr target = new LocalExpr(operand.index(), expr.type()); + addStore(target, expr); + } + + public void visit_fstore(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr expr = stack.pop(Type.FLOAT); + final LocalExpr target = new LocalExpr(operand.index(), expr.type()); + addStore(target, expr); + } + + public void visit_dstore(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + final Expr expr = stack.pop(Type.DOUBLE); + final LocalExpr target = new LocalExpr(operand.index(), 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 visit_astore(final Instruction inst) { + final LocalVariable operand = (LocalVariable) inst.operand(); + + Expr expr = stack.peek(); + + if (expr.type().isAddress()) { + Assert.isTrue(sub != null); + Assert.isTrue(!saveValue); + expr = stack.pop(Type.ADDRESS); + sub.setReturnAddress(operand); + addStmt(new AddressStoreStmt(sub)); + } else { + expr = stack.pop(Type.OBJECT); + final LocalExpr target = new LocalExpr(operand.index(), expr.type()); + addStore(target, expr); + } + } + + public void visit_iastore(final Instruction inst) { + final Expr value = stack.pop(Type.INTEGER); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.INTEGER.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, + Type.INTEGER, Type.INTEGER); + addStore(target, value); + } + + public void visit_lastore(final Instruction inst) { + final Expr value = stack.pop(Type.LONG); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.LONG.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, Type.LONG, + Type.LONG); + addStore(target, value); + } + + public void visit_fastore(final Instruction inst) { + final Expr value = stack.pop(Type.FLOAT); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.FLOAT.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, Type.FLOAT, + Type.FLOAT); + addStore(target, value); + } + + public void visit_dastore(final Instruction inst) { + final Expr value = stack.pop(Type.DOUBLE); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.DOUBLE.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, Type.DOUBLE, + Type.DOUBLE); + addStore(target, value); + } + + public void visit_aastore(final Instruction inst) { + final Expr value = stack.pop(Type.OBJECT); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.OBJECT.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, Type.OBJECT, + Type.OBJECT); + addStore(target, value); + } + + public void visit_bastore(final Instruction inst) { + final Expr value = stack.pop(Type.BYTE); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.BYTE.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, Type.BYTE, + Type.BYTE); + addStore(target, value); + } + + public void visit_castore(final Instruction inst) { + final Expr value = stack.pop(Type.CHARACTER); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.CHARACTER.arrayType()); + final ArrayRefExpr target = new ArrayRefExpr(array, index, + Type.CHARACTER, Type.CHARACTER); + addStore(target, value); + } + + public void visit_sastore(final Instruction inst) { + final Expr value = stack.pop(Type.SHORT); + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.SHORT.arrayType()); + final 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 visit_pop(final Instruction inst) { + final Expr expr = stack.pop1(); + addStmt(new ExprStmt(expr)); + } + + public void visit_pop2(final Instruction inst) { + final 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 visit_dup(final Instruction inst) { + // 0 -> 0 0 + + db(" dup"); + + if (Tree.USE_STACK) { + saveStack(); + + final StackExpr s0 = (StackExpr) stack.pop1(); + + final StackExpr[] s = new StackExpr[] { s0 }; + manip(s, new int[] { 0, 0 }, StackManipStmt.DUP); + + } else { + final Expr s0 = stack.pop1(); + + final 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 visit_dup_x1(final Instruction inst) { + // 0 1 -> 1 0 1 + + if (Tree.USE_STACK) { + saveStack(); + + final StackExpr s1 = (StackExpr) stack.pop1(); + final StackExpr s0 = (StackExpr) stack.pop1(); + + final StackExpr[] s = new StackExpr[] { s0, s1 }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP_X1); + + } else { + final Expr s1 = stack.pop1(); + final Expr s0 = stack.pop1(); + + final LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + final 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 visit_dup_x2(final Instruction inst) { + // 0 1 2 -> 2 0 1 2 + // 0-1 2 -> 2 0-1 2 + + db(" dup_x2"); + + if (Tree.USE_STACK) { + saveStack(); + + final StackExpr s2 = (StackExpr) stack.pop1(); + final Expr[] s01 = stack.pop2(); + + if (s01.length == 2) { + // 0 1 2 -> 2 0 1 2 + final StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1], s2 }; + manip(s, new int[] { 2, 0, 1, 2 }, StackManipStmt.DUP_X2); + } else { + // 0-1 2 -> 2 0-1 2 + final StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], s2 }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP_X2); + } + + } else { + final Expr s2 = stack.pop1(); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1] + .type()); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final 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 visit_dup2(final Instruction inst) { + // 0 1 -> 0 1 0 1 + // 0-1 -> 0-1 0-1 + + if (Tree.USE_STACK) { + saveStack(); + + final Expr[] s01 = stack.pop2(); + + if (s01.length == 1) { + // 0-1 -> 0-1 0-1 + final 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); + + final StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s01[1] }; + manip(s, new int[] { 0, 1, 0, 1 }, StackManipStmt.DUP2); + } + } else { + final Expr[] s01 = stack.pop2(); + + if (s01.length == 1) { + // 0-1 -> 0-1 0-1 + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final 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 visit_dup2_x1(final Instruction inst) { + // 0 1 2 -> 1 2 0 1 2 + // 0 1-2 -> 1-2 0 1-2 + + if (Tree.USE_STACK) { + saveStack(); + + final Expr[] s12 = stack.pop2(); + final StackExpr s0 = (StackExpr) stack.pop1(); + + if (s12.length == 2) { + // 0 1 2 -> 1 2 0 1 2 + final StackExpr[] s = new 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 + final StackExpr[] s = new StackExpr[] { s0, (StackExpr) s12[0] }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP2_X1); + } + } else { + final Expr[] s12 = stack.pop2(); + final StackExpr s0 = (StackExpr) stack.pop1(); + + if (s12.length == 2) { + // 0 1 2 -> 1 2 0 1 2 + final LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + final LocalExpr t1 = newStackLocal(stack.height() + 1, s12[0] + .type()); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + final 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 visit_dup2_x2(final Instruction 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 (Tree.USE_STACK) { + saveStack(); + + final Expr[] s23 = stack.pop2(); + final Expr[] s01 = stack.pop2(); + + if ((s01.length == 2) && (s23.length == 2)) { + // 0 1 2 3 -> 2 3 0 1 2 3 + final 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 + final 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 + final 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 + final StackExpr[] s = new StackExpr[] { (StackExpr) s01[0], + (StackExpr) s23[0] }; + manip(s, new int[] { 1, 0, 1 }, StackManipStmt.DUP2_X2); + } + } else { + final Expr[] s23 = stack.pop2(); + final Expr[] s01 = stack.pop2(); + + if ((s01.length == 2) && (s23.length == 2)) { + // 0 1 2 3 -> 2 3 0 1 2 3 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1] + .type()); + final LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0] + .type()); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final LocalExpr t1 = newStackLocal(stack.height() + 1, s01[1] + .type()); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final LocalExpr t2 = newStackLocal(stack.height() + 2, s23[0] + .type()); + final 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 + final LocalExpr t0 = newStackLocal(stack.height(), s01[0] + .type()); + final 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 visit_swap(final Instruction inst) { + // 0 1 -> 1 0 + + if (Tree.USE_STACK) { + saveStack(); + + final StackExpr s1 = (StackExpr) stack.pop1(); + final StackExpr s0 = (StackExpr) stack.pop1(); + + final StackExpr[] s = new StackExpr[] { s0, s1 }; + manip(s, new int[] { 1, 0 }, StackManipStmt.SWAP); + } else { + final Expr s1 = stack.pop1(); + final Expr s0 = stack.pop1(); + + final LocalExpr t0 = newStackLocal(stack.height(), s0.type()); + final 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(final StackExpr[] source, final int[] s, final int kind) { + Assert.isTrue(Tree.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++) { + final Expr expr = stack.get(i); + height += expr.type().stackHeight(); + } + + // 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). + final StackExpr[] target = new StackExpr[s.length]; + + for (int i = 0; i < s.length; i++) { + target[i] = new StackExpr(height, source[s[i]].type()); + final StackExpr copy = (StackExpr) target[i].clone(); + copy.setDef(null); + stack.push(copy); + height += target[i].type().stackHeight(); + } + + 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 visit_iadd(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_ladd(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.LONG); + stack.push(top); + } + + public void visit_fadd(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.FLOAT); + stack.push(top); + } + + public void visit_dadd(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.ADD, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visit_isub(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_lsub(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.LONG); + stack.push(top); + } + + public void visit_fsub(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.FLOAT); + stack.push(top); + } + + public void visit_dsub(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.SUB, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visit_imul(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_lmul(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.LONG); + stack.push(top); + } + + public void visit_fmul(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.FLOAT); + stack.push(top); + } + + public void visit_dmul(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.MUL, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visit_idiv(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr check = new ZeroCheckExpr(right, Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.DIV, left, check, Type.INTEGER); + stack.push(top); + } + + public void visit_ldiv(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr check = new ZeroCheckExpr(right, Type.LONG); + final Expr top = new ArithExpr(ArithExpr.DIV, left, check, Type.LONG); + stack.push(top); + } + + public void visit_fdiv(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.DIV, left, right, Type.FLOAT); + stack.push(top); + } + + public void visit_ddiv(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.DIV, left, right, Type.DOUBLE); + stack.push(top); + } + + public void visit_irem(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr check = new ZeroCheckExpr(right, Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.REM, left, check, Type.INTEGER); + stack.push(top); + } + + public void visit_lrem(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr check = new ZeroCheckExpr(right, Type.LONG); + final Expr top = new ArithExpr(ArithExpr.REM, left, check, Type.LONG); + stack.push(top); + } + + public void visit_frem(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.REM, left, right, Type.FLOAT); + stack.push(top); + } + + public void visit_drem(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final 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 visit_ineg(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new NegExpr(expr, Type.INTEGER); + stack.push(top); + } + + public void visit_lneg(final Instruction inst) { + final Expr expr = stack.pop(Type.LONG); + final Expr top = new NegExpr(expr, Type.LONG); + stack.push(top); + } + + public void visit_fneg(final Instruction inst) { + final Expr expr = stack.pop(Type.FLOAT); + final Expr top = new NegExpr(expr, Type.FLOAT); + stack.push(top); + } + + public void visit_dneg(final Instruction inst) { + final Expr expr = stack.pop(Type.DOUBLE); + final Expr top = new NegExpr(expr, Type.DOUBLE); + stack.push(top); + } + + /** + * All visit_xshd push a ShiftExpr onto the + * operand stack. + * + * @see ShiftExpr + */ + public void visit_ishl(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ShiftExpr(ShiftExpr.LEFT, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_lshl(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ShiftExpr(ShiftExpr.LEFT, left, right, Type.LONG); + stack.push(top); + } + + public void visit_ishr(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ShiftExpr(ShiftExpr.RIGHT, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_lshr(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ShiftExpr(ShiftExpr.RIGHT, left, right, Type.LONG); + stack.push(top); + } + + public void visit_iushr(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ShiftExpr(ShiftExpr.UNSIGNED_RIGHT, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_lushr(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.LONG); + final 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 visit_iand(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.AND, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_land(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.AND, left, right, Type.LONG); + stack.push(top); + } + + public void visit_ior(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.IOR, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_lor(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.IOR, left, right, Type.LONG); + stack.push(top); + } + + public void visit_ixor(final Instruction inst) { + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Expr top = new ArithExpr(ArithExpr.XOR, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_lxor(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final 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 visit_iinc(final Instruction inst) { + final IncOperand operand = (IncOperand) inst.operand(); + int incr = operand.incr(); + + if (incr < 0) { + final Expr right = new ConstantExpr(new Integer(-incr), + Type.INTEGER); + final Expr left = new LocalExpr(operand.var().index(), Type.INTEGER); + + final Expr top = new ArithExpr(ArithExpr.SUB, left, right, + Type.INTEGER); + + final LocalExpr copy = (LocalExpr) left.clone(); + copy.setDef(null); + addStmt(new ExprStmt(new StoreExpr(copy, top, left.type()))); + } else if (incr > 0) { + final Expr right = new ConstantExpr(new Integer(incr), Type.INTEGER); + final Expr left = new LocalExpr(operand.var().index(), Type.INTEGER); + + final Expr top = new ArithExpr(ArithExpr.ADD, left, right, + Type.INTEGER); + + final 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 visit_i2l(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visit_i2f(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visit_i2d(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visit_l2i(final Instruction inst) { + final Expr expr = stack.pop(Type.LONG); + final Expr top = new CastExpr(expr, Type.INTEGER, Type.INTEGER); + stack.push(top); + } + + public void visit_l2f(final Instruction inst) { + final Expr expr = stack.pop(Type.LONG); + final Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visit_l2d(final Instruction inst) { + final Expr expr = stack.pop(Type.LONG); + final Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visit_f2i(final Instruction inst) { + final Expr expr = stack.pop(Type.FLOAT); + final Expr top = new CastExpr(expr, Type.INTEGER, Type.INTEGER); + stack.push(top); + } + + public void visit_f2l(final Instruction inst) { + final Expr expr = stack.pop(Type.FLOAT); + final Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visit_f2d(final Instruction inst) { + final Expr expr = stack.pop(Type.FLOAT); + final Expr top = new CastExpr(expr, Type.DOUBLE, Type.DOUBLE); + stack.push(top); + } + + public void visit_d2i(final Instruction inst) { + final Expr expr = stack.pop(Type.DOUBLE); + final Expr top = new CastExpr(expr, Type.INTEGER, Type.INTEGER); + stack.push(top); + } + + public void visit_d2l(final Instruction inst) { + final Expr expr = stack.pop(Type.DOUBLE); + final Expr top = new CastExpr(expr, Type.LONG, Type.LONG); + stack.push(top); + } + + public void visit_d2f(final Instruction inst) { + final Expr expr = stack.pop(Type.DOUBLE); + final Expr top = new CastExpr(expr, Type.FLOAT, Type.FLOAT); + stack.push(top); + } + + public void visit_i2b(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.BYTE, Type.INTEGER); + stack.push(top); + } + + public void visit_i2c(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.CHARACTER, Type.INTEGER); + stack.push(top); + } + + public void visit_i2s(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + final Expr top = new CastExpr(expr, Type.SHORT, Type.INTEGER); + stack.push(top); + } + + /** + * All visit_xcmp push an ArithExpr onto the stack. + * + * @see ArithExpr + */ + public void visit_lcmp(final Instruction inst) { + final Expr right = stack.pop(Type.LONG); + final Expr left = stack.pop(Type.LONG); + final Expr top = new ArithExpr(ArithExpr.CMP, left, right, Type.INTEGER); + stack.push(top); + } + + public void visit_fcmpl(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.CMPL, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_fcmpg(final Instruction inst) { + final Expr right = stack.pop(Type.FLOAT); + final Expr left = stack.pop(Type.FLOAT); + final Expr top = new ArithExpr(ArithExpr.CMPG, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_dcmpl(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.CMPL, left, right, + Type.INTEGER); + stack.push(top); + } + + public void visit_dcmpg(final Instruction inst) { + final Expr right = stack.pop(Type.DOUBLE); + final Expr left = stack.pop(Type.DOUBLE); + final Expr top = new ArithExpr(ArithExpr.CMPG, left, right, + Type.INTEGER); + stack.push(top); + } + + /** + * All visit_xeg add an IfZeroStmt to the statement + * list. + * + * @see IfZeroStmt + */ + public void visit_ifeq(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.EQ, left, t, next)); + } + + public void visit_ifne(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.NE, left, t, next)); + } + + public void visit_iflt(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.LT, left, t, next)); + } + + public void visit_ifge(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.GE, left, t, next)); + } + + public void visit_ifgt(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.GT, left, t, next)); + } + + public void visit_ifle(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.LE, left, t, next)); + } + + /** + * All visit_if_xcmpy add a IfCmpStmt to the + * statement list. + */ + public void visit_if_icmpeq(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.EQ, left, right, t, next)); + } + + public void visit_if_icmpne(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.NE, left, right, t, next)); + } + + public void visit_if_icmplt(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.LT, left, right, t, next)); + } + + public void visit_if_icmpge(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.GE, left, right, t, next)); + } + + public void visit_if_icmpgt(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.GT, left, right, t, next)); + } + + public void visit_if_icmple(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.INTEGER); + final Expr left = stack.pop(Type.INTEGER); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.LE, left, right, t, next)); + } + + public void visit_if_acmpeq(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.OBJECT); + final Expr left = stack.pop(Type.OBJECT); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.EQ, left, right, t, next)); + } + + public void visit_if_acmpne(final Instruction inst) { + // 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(); + + final Expr right = stack.pop(Type.OBJECT); + final Expr left = stack.pop(Type.OBJECT); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfCmpStmt(IfStmt.NE, left, right, t, next)); + } + + /** + * Adds a GotoStmt to the statement list. + * + * @see GotoStmt + */ + public void visit_goto(final Instruction inst) { + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + addStmt(new GotoStmt(t)); + } + + /** + * Adds a JsrStmt to the statement list. + * + * @see JsrStmt + */ + public void visit_jsr(final Instruction inst) { + // 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. + final Subroutine sub = block.graph().labelSub((Label) inst.operand()); + addStmt(new JsrStmt(sub, next)); + stack.push(new ReturnAddressExpr(Type.ADDRESS)); + } + + /** + * Adds a RetStmt to the statement list. + * + * @see RetStmt + */ + public void visit_ret(final Instruction inst) { + Assert.isTrue(sub != null); + addStmt(new RetStmt(sub)); + } + + /** + * Add a SwitchStmt to the statement list. + * + * @see SwitchStmt + */ + public void visit_switch(final Instruction inst) { + // 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(); + + final Expr index = stack.pop(Type.INTEGER); + + final Switch sw = (Switch) inst.operand(); + + final Block defaultTarget = (Block) block.graph().getNode( + sw.defaultTarget()); + Assert.isTrue(defaultTarget != null, "No block for " + inst); + + final Block[] targets = new Block[sw.targets().length]; + + for (int i = 0; i < targets.length; i++) { + targets[i] = (Block) block.graph().getNode(sw.targets()[i]); + Assert.isTrue(targets[i] != null, "No block for " + inst); + } + + addStmt(new SwitchStmt(index, defaultTarget, targets, sw.values())); + } + + /** + * All visit_xreturn add a ReturnExprStmt to the + * statement list. + * + * @see ReturnExprStmt + */ + public void visit_ireturn(final Instruction inst) { + final Expr expr = stack.pop(Type.INTEGER); + addStmt(new ReturnExprStmt(expr)); + } + + public void visit_lreturn(final Instruction inst) { + final Expr expr = stack.pop(Type.LONG); + addStmt(new ReturnExprStmt(expr)); + } + + public void visit_freturn(final Instruction inst) { + final Expr expr = stack.pop(Type.FLOAT); + addStmt(new ReturnExprStmt(expr)); + } + + public void visit_dreturn(final Instruction inst) { + final Expr expr = stack.pop(Type.DOUBLE); + addStmt(new ReturnExprStmt(expr)); + } + + public void visit_areturn(final Instruction inst) { + final Expr expr = stack.pop(Type.OBJECT); + addStmt(new ReturnExprStmt(expr)); + } + + /** + * Adds a ReturnStmt to the statement list. + */ + public void visit_return(final Instruction inst) { + addStmt(new ReturnStmt()); + } + + /** + * Pushes a StaticFieldExpr onto the operand stack. + */ + public void visit_getstatic(final Instruction inst) { + final MemberRef field = (MemberRef) inst.operand(); + final Type type = field.nameAndType().type(); + + try { + final EditorContext context = block.graph().method() + .declaringClass().context(); + final FieldEditor e = context.editField(field); + + if (e.isFinal()) { + if (e.constantValue() != null) { + final Expr top = new ConstantExpr(e.constantValue(), type); + stack.push(top); + context.release(e.fieldInfo()); + return; + } + } + + context.release(e.fieldInfo()); + } catch (final NoSuchFieldException e) { + // No field found. Silently assume non-final. + } + + final Expr top = new StaticFieldExpr(field, type); + stack.push(top); + } + + public void visit_putstatic(final Instruction inst) { + final MemberRef field = (MemberRef) inst.operand(); + final Type type = field.nameAndType().type(); + final Expr value = stack.pop(type); + final StaticFieldExpr target = new StaticFieldExpr(field, type); + addStore(target, value); + } + + public void visit_putstatic_nowb(final Instruction inst) { + visit_putstatic(inst); + } + + /** + * Pushes a FieldExpr onto the operand stack. + */ + public void visit_getfield(final Instruction inst) { + final MemberRef field = (MemberRef) inst.operand(); + final Type type = field.nameAndType().type(); + final Expr obj = stack.pop(Type.OBJECT); + final Expr check = new ZeroCheckExpr(obj, obj.type()); + final Expr top = new FieldExpr(check, field, type); + stack.push(top); + } + + public void visit_putfield(final Instruction inst) { + final MemberRef field = (MemberRef) inst.operand(); + final Type type = field.nameAndType().type(); + final Expr value = stack.pop(type); + final Expr obj = stack.pop(Type.OBJECT); + Expr ucCheck = obj; + + if (Tree.USE_PERSISTENT) { + ucCheck = new UCExpr(obj, UCExpr.POINTER, obj.type()); + } + + final Expr check = new ZeroCheckExpr(ucCheck, obj.type()); + final FieldExpr target = new FieldExpr(check, field, type); + addStore(target, value); + } + + // Don't insert UCExpr + public void visit_putfield_nowb(final Instruction inst) { + final MemberRef field = (MemberRef) inst.operand(); + final Type type = field.nameAndType().type(); + final Expr value = stack.pop(type); + final Expr obj = stack.pop(Type.OBJECT); + final Expr check = new ZeroCheckExpr(obj, obj.type()); + final 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 visit_invokevirtual(final Instruction inst) { + addCall(inst, CallMethodExpr.VIRTUAL); + } + + public void visit_invokespecial(final Instruction inst) { + addCall(inst, CallMethodExpr.NONVIRTUAL); + } + + public void visit_invokestatic(final Instruction inst) { + addCall(inst, 0); + } + + public void visit_invokeinterface(final Instruction 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(final Instruction inst, final int kind) { + final MemberRef method = (MemberRef) inst.operand(); + final Type type = method.nameAndType().type(); + + final Type[] paramTypes = type.paramTypes(); + final Expr[] params = new Expr[paramTypes.length]; + + for (int i = paramTypes.length - 1; i >= 0; i--) { + params[i] = stack.pop(paramTypes[i]); + } + + Expr top; + + if (inst.opcodeClass() != Opcode.opcx_invokestatic) { + final Expr obj = stack.pop(Type.OBJECT); + + top = new CallMethodExpr(kind, obj, params, method, type + .returnType()); + + } else { + top = new CallStaticExpr(params, method, type.returnType()); + } + + if (type.returnType().equals(Type.VOID)) { + addStmt(new ExprStmt(top)); + + } else { + stack.push(top); + } + } + + /** + * Pushes a NewExpr onto the operand stack. + * + * @see NewExpr + */ + public void visit_new(final Instruction inst) { + final Type type = (Type) inst.operand(); + final Expr top = new NewExpr(type, Type.OBJECT); + stack.push(top); + + db(" new: " + top); + } + + /** + * Pushes a NewArrayExpr onto the operand stack. + */ + public void visit_newarray(final Instruction inst) { + final Type type = (Type) inst.operand(); + final Expr size = stack.pop(Type.INTEGER); + final Expr top = new NewArrayExpr(size, type, type.arrayType()); + stack.push(top); + } + + /** + * Pushes an ArrayLengthExpr onto the operand stack. + * + * @see ArrayLengthExpr + */ + public void visit_arraylength(final Instruction inst) { + final Expr array = stack.pop(Type.OBJECT); + final Expr top = new ArrayLengthExpr(array, Type.INTEGER); + stack.push(top); + } + + /** + * Adds a ThrowStmt to the statement list. + * + * @see ThrowStmt + */ + public void visit_athrow(final Instruction inst) { + final Expr expr = stack.pop(Type.THROWABLE); + addStmt(new ThrowStmt(expr)); + } + + /** + * Pushes a CastExpr onto the operand stack. + * + * @see CastExpr + */ + public void visit_checkcast(final Instruction inst) { + final Expr expr = stack.pop(Type.OBJECT); + final Type type = (Type) inst.operand(); + final Expr top = new CastExpr(expr, type, type); + stack.push(top); + } + + /** + * Pushes an InstanceOfExpr onto the operand stack. + * + * @see InstanceOfExpr + */ + public void visit_instanceof(final Instruction inst) { + final Type type = (Type) inst.operand(); + final Expr expr = stack.pop(Type.OBJECT); + final Expr top = new InstanceOfExpr(expr, type, Type.INTEGER); + stack.push(top); + } + + /** + * Both monitor visitors add a MonitorStmt to the statement list. + * + * @see MonitorStmt + */ + public void visit_monitorenter(final Instruction inst) { + final Expr obj = stack.pop(Type.OBJECT); + addStmt(new MonitorStmt(MonitorStmt.ENTER, obj)); + } + + public void visit_monitorexit(final Instruction inst) { + final Expr obj = stack.pop(Type.OBJECT); + addStmt(new MonitorStmt(MonitorStmt.EXIT, obj)); + } + + /** + * Push a NewMultiArrayExpr onto the operand stack. + * + * @see NewMultiArrayExpr + */ + public void visit_multianewarray(final Instruction inst) { + final MultiArrayOperand operand = (MultiArrayOperand) inst.operand(); + + final Expr[] dim = new Expr[operand.dimensions()]; + + for (int i = dim.length - 1; i >= 0; i--) { + dim[i] = stack.pop(Type.INTEGER); + } + + final Type type = operand.type(); + + final Expr top = new NewMultiArrayExpr(dim, type + .elementType(dim.length), type); + + stack.push(top); + } + + /** + * Both visit_xnull add an IfZeroStmt to the statement + * list. + * + * ssee IfZeroStmt + */ + public void visit_ifnull(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.OBJECT); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.EQ, left, t, next)); + } + + public void visit_ifnonnull(final Instruction inst) { + // 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(); + + final Expr left = stack.pop(Type.OBJECT); + final Block t = (Block) block.graph().getNode(inst.operand()); + Assert.isTrue(t != null, "No block for " + inst); + + addStmt(new IfZeroStmt(IfStmt.NE, left, t, next)); + } + + /** + * Replaces the expression on the top of the stack with an RCExpr. + * + * @see RCExpr + */ + public void visit_rc(final Instruction inst) { + final Integer depth = (Integer) inst.operand(); + final Expr object = stack.peek(depth.intValue()); + stack.replace(depth.intValue(), new RCExpr(object, object.type())); + } + + /** + * + */ + public void visit_aupdate(final Instruction inst) { + Integer depth = (Integer) inst.operand(); + + if (Tree.AUPDATE_FIX_HACK) { + // Hack to fix a bug in old bloat-generated code: + + if (depth.intValue() == 1) { + final Expr object = stack.peek(); + + if (object.type().isWide()) { + depth = new Integer(2); + inst.setOperand(depth); + Tree.AUPDATE_FIX_HACK_CHANGED = true; + } + } + } + + final 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 + */ + public void visit_supdate(final Instruction inst) { + final Integer depth = (Integer) inst.operand(); + final 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 + */ + public void visit_aswizzle(final Instruction inst) { + final Expr index = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.OBJECT.arrayType()); + + addStmt(new SCStmt(array, index)); + + } + + /** + * Add a SRStmt to the statement list. + * + * @see SRStmt + */ + public void visit_aswrange(final Instruction inst) { + final Expr end = stack.pop(Type.INTEGER); + final Expr start = stack.pop(Type.INTEGER); + final Expr array = stack.pop(Type.OBJECT.arrayType()); + + addStmt(new SRStmt(array, start, end)); + } + + /** + * Visit all the statements in the statement list. + */ + public void visitForceChildren(final TreeVisitor visitor) { + final LinkedList list = new LinkedList(stmts); + + if (visitor.reverse()) { + final ListIterator iter = list.listIterator(stmts.size()); + + while (iter.hasPrevious()) { + final Stmt s = (Stmt) iter.previous(); + s.visit(visitor); + } + } else { + final ListIterator iter = list.listIterator(); + + while (iter.hasNext()) { + final Stmt s = (Stmt) iter.next(); + s.visit(visitor); + } + } + } + + public void visit(final TreeVisitor visitor) { + visitor.visitTree(this); + } + + public Node parent() { + return null; + } + + public Block block() { + return block; + } +} 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..1336ade --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/TreeVisitor.java @@ -0,0 +1,309 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.cfg.*; + +/** + * 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(TreeVisitor.FORWARD); + } + + public TreeVisitor(final int direction) { + this.direction = direction; + } + + /** + * @param prune + * Is the tree pruned during traversal? + */ + public void setPrune(final 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 == TreeVisitor.FORWARD; + } + + public boolean reverse() { + return direction == TreeVisitor.REVERSE; + } + + public void visitFlowGraph(final FlowGraph graph) { + graph.visitChildren(this); + } + + public void visitBlock(final Block block) { + block.visitChildren(this); + } + + public void visitTree(final Tree tree) { + visitNode(tree); + } + + public void visitExprStmt(final ExprStmt stmt) { + visitStmt(stmt); + } + + public void visitIfStmt(final IfStmt stmt) { + visitStmt(stmt); + } + + public void visitIfCmpStmt(final IfCmpStmt stmt) { + visitIfStmt(stmt); + } + + public void visitIfZeroStmt(final IfZeroStmt stmt) { + visitIfStmt(stmt); + } + + public void visitInitStmt(final InitStmt stmt) { + visitStmt(stmt); + } + + public void visitGotoStmt(final GotoStmt stmt) { + visitStmt(stmt); + } + + public void visitLabelStmt(final LabelStmt stmt) { + visitStmt(stmt); + } + + public void visitMonitorStmt(final MonitorStmt stmt) { + visitStmt(stmt); + } + + public void visitPhiStmt(final PhiStmt stmt) { + visitStmt(stmt); + } + + public void visitCatchExpr(final CatchExpr expr) { + visitExpr(expr); + } + + public void visitDefExpr(final DefExpr expr) { + visitExpr(expr); + } + + public void visitStackManipStmt(final StackManipStmt stmt) { + visitStmt(stmt); + } + + public void visitPhiCatchStmt(final PhiCatchStmt stmt) { + visitPhiStmt(stmt); + } + + public void visitPhiJoinStmt(final PhiJoinStmt stmt) { + visitPhiStmt(stmt); + } + + public void visitRetStmt(final RetStmt stmt) { + visitStmt(stmt); + } + + public void visitReturnExprStmt(final ReturnExprStmt stmt) { + visitStmt(stmt); + } + + public void visitReturnStmt(final ReturnStmt stmt) { + visitStmt(stmt); + } + + public void visitAddressStoreStmt(final AddressStoreStmt stmt) { + visitStmt(stmt); + } + + public void visitStoreExpr(final StoreExpr expr) { + visitExpr(expr); + } + + public void visitJsrStmt(final JsrStmt stmt) { + visitStmt(stmt); + } + + public void visitSwitchStmt(final SwitchStmt stmt) { + visitStmt(stmt); + } + + public void visitThrowStmt(final ThrowStmt stmt) { + visitStmt(stmt); + } + + public void visitStmt(final Stmt stmt) { + visitNode(stmt); + } + + public void visitSCStmt(final SCStmt stmt) { + visitStmt(stmt); + } + + public void visitSRStmt(final SRStmt stmt) { + visitStmt(stmt); + } + + public void visitArithExpr(final ArithExpr expr) { + visitExpr(expr); + } + + public void visitArrayLengthExpr(final ArrayLengthExpr expr) { + visitExpr(expr); + } + + public void visitMemExpr(final MemExpr expr) { + visitDefExpr(expr); + } + + public void visitMemRefExpr(final MemRefExpr expr) { + visitMemExpr(expr); + } + + public void visitArrayRefExpr(final ArrayRefExpr expr) { + visitMemRefExpr(expr); + } + + public void visitCallExpr(final CallExpr expr) { + visitExpr(expr); + } + + public void visitCallMethodExpr(final CallMethodExpr expr) { + visitCallExpr(expr); + } + + public void visitCallStaticExpr(final CallStaticExpr expr) { + visitCallExpr(expr); + } + + public void visitCastExpr(final CastExpr expr) { + visitExpr(expr); + } + + public void visitConstantExpr(final ConstantExpr expr) { + visitExpr(expr); + } + + public void visitFieldExpr(final FieldExpr expr) { + visitMemRefExpr(expr); + } + + public void visitInstanceOfExpr(final InstanceOfExpr expr) { + visitExpr(expr); + } + + public void visitLocalExpr(final LocalExpr expr) { + visitVarExpr(expr); + } + + public void visitNegExpr(final NegExpr expr) { + visitExpr(expr); + } + + public void visitNewArrayExpr(final NewArrayExpr expr) { + visitExpr(expr); + } + + public void visitNewExpr(final NewExpr expr) { + visitExpr(expr); + } + + public void visitNewMultiArrayExpr(final NewMultiArrayExpr expr) { + visitExpr(expr); + } + + public void visitCheckExpr(final CheckExpr expr) { + visitExpr(expr); + } + + public void visitZeroCheckExpr(final ZeroCheckExpr expr) { + visitCheckExpr(expr); + } + + public void visitRCExpr(final RCExpr expr) { + visitCheckExpr(expr); + } + + public void visitUCExpr(final UCExpr expr) { + visitCheckExpr(expr); + } + + public void visitReturnAddressExpr(final ReturnAddressExpr expr) { + visitExpr(expr); + } + + public void visitShiftExpr(final ShiftExpr expr) { + visitExpr(expr); + } + + public void visitStackExpr(final StackExpr expr) { + visitVarExpr(expr); + } + + public void visitVarExpr(final VarExpr expr) { + visitMemExpr(expr); + } + + public void visitStaticFieldExpr(final StaticFieldExpr expr) { + visitMemRefExpr(expr); + } + + public void visitExpr(final Expr expr) { + visitNode(expr); + } + + public void visitNode(final 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..91f7514 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Type0Visitor.java @@ -0,0 +1,132 @@ +/** + * 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. + * + * 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 + */ + +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(final Hashtable defInfoMap, final Hashtable useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public boolean search(final LocalExpr start) { + this.start = start; + previous = this.start; + found = false; + this.start.parent().visit(this); + return found; + } + + public void check(final Node node) { + + if (node instanceof ExprStmt) { + 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 + final 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 + } else { + check(n.target()); + } + } + + else if (node instanceof InitStmt) { // if it's an InitStmt, + final LocalExpr[] targets = ((InitStmt) node).targets(); // check + // the + // last + if (targets.length > 0) { + 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(final Hashtable useInfoMap, + final Hashtable defInfoMap) { + super(useInfoMap, defInfoMap); + } + + public void visitLocalExpr(final LocalExpr expr) { + if ((expr.index() == start.index()) && (expr.def() == start.def())) { + // we've found a match + // update information + ((UseInformation) useInfoMap.get(start)).type = 0; + final 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..9a3ac00 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/Type1Visitor.java @@ -0,0 +1,165 @@ +/** + * 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. + * + * 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 + */ + +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(final Hashtable defInfoMap, final Hashtable useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public void search(final 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(final Node node) { + + if ((node instanceof Expr) && ((Expr) node).type().isWide()) { + turningPoint = null; + return; // give up. We cannot swap around a wide value. + } + + turningPoint = node; + + if (node instanceof StoreExpr) { + check(((StoreExpr) node).expr()); // to search down the expression + // being stored + } 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(final Hashtable defInfoMap, final Hashtable useInfoMap) { + super(defInfoMap, useInfoMap); + } + + public void search(final Node turningPoint, final LocalExpr start) { + + found = false; + this.start = start; + previous = turningPoint; + this.turningPoint = turningPoint; + if ((turningPoint.parent() != null) + && !(turningPoint.parent() instanceof Tree)) { + // 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(final Node node) { + + if (node instanceof ExprStmt) { + check(((ExprStmt) node).expr()); // might be something we want + } else if (node instanceof StoreExpr) { + check(((StoreExpr) node).target()); // to see if the target matches + } 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(final Hashtable useInfoMap, + final Hashtable defInfoMap) { + super(useInfoMap, defInfoMap); + } + + public void visitLocalExpr(final LocalExpr expr) { + + if ((expr.index() == start.index()) && (expr.def() == start.def())) { + // we've found a match + // update information + ((UseInformation) useInfoMap.get(start)).type = 1; + final 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..53c8258 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/UCExpr.java @@ -0,0 +1,67 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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; + + 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(final Expr expr, final int kind, final Type type) { + super(expr, type); + this.kind = kind; + } + + public int kind() { + return kind; + } + + public void visit(final TreeVisitor visitor) { + visitor.visitUCExpr(this); + } + + public boolean equalsExpr(final 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..85b6009 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/UseInformation.java @@ -0,0 +1,54 @@ +/** + * 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. + * + * 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 + */ + +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..12da2f2 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/VarExpr.java @@ -0,0 +1,70 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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(final int index, final Type type) { + super(type); + this.index = index; + } + + public void setIndex(final 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..a1a7b61 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/tree/ZeroCheckExpr.java @@ -0,0 +1,56 @@ +/** + * 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. + * + * 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 + */ + +package EDU.purdue.cs.bloat.tree; + +import EDU.purdue.cs.bloat.editor.*; + +/** + * 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/.cvsignore b/src/EDU/purdue/cs/bloat/util/.cvsignore new file mode 100644 index 0000000..5241a72 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/.cvsignore @@ -0,0 +1 @@ +*.class \ 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..0484048 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/Assert.java @@ -0,0 +1,74 @@ +/* + * 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. + * + * 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 + */ +package EDU.purdue.cs.bloat.util; + +/** + * 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, final String msg) { + if (!test) { + throw new IllegalArgumentException("Assert.isTrue: " + msg); + } + } + + public static void isFalse(final boolean test, final String msg) { + if (test) { + throw new IllegalArgumentException("Assert.isFalse: " + msg); + } + } + + public static void isNotNull(final Object test, final String msg) { + if (test == null) { + throw new IllegalArgumentException("Assert.isNotNull: " + msg); + } + } + + public static void isNull(final Object test, final 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(final boolean test) { + if (test) { + throw new IllegalArgumentException("Assert.isFalse failed"); + } + } + + public static void isNotNull(final Object test) { + if (test == null) { + throw new IllegalArgumentException("Assert.isNotNull failed"); + } + } + + public static void isNull(final Object test) { + if (test != null) { + throw new IllegalArgumentException("Assert.isNull failed"); + } + } +} 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..272f2f6 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/Graph.java @@ -0,0 +1,948 @@ +/* + * 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. + * + * 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 + */ + +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(final Collection c, final boolean reverse) { + final HashSet visited = new HashSet(nodes.size() * 2); + final ArrayList stack = new ArrayList(); + + final Iterator iter = nodes.values().iterator(); + + while (iter.hasNext()) { + final GraphNode node = (GraphNode) iter.next(); + + if (!visited.contains(node)) { + visited.add(node); + stack.add(node); + + while (!stack.isEmpty()) { + final GraphNode v = (GraphNode) stack + .remove(stack.size() - 1); + boolean pushed = false; + + final Iterator preds = reverse ? v.succs.iterator() + : v.preds.iterator(); + + while (preds.hasNext()) { + final 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(final GraphNode v) { + return new EdgeSet(v, v.succs); + } + + /** + * Returns the predacessors of a given node. + */ + public Collection preds(final 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(final GraphNode v, final 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(final 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(final 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(); + + final Set visited = new HashSet(); + + // Calculate the indices of the nodes. + while (iter.hasNext()) { + final 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()) { + final 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(); + } + + final Iterator iter = nodes.entrySet().iterator(); + + while (iter.hasNext()) { + final Map.Entry e = (Map.Entry) iter.next(); + + final 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(final GraphNode node, final Set visited) { + visited.add(node); + + // Visit in pre-order + node.setPreOrderIndex(preOrder.size()); + preOrder.addNode(node); + + final Iterator iter = succs(node).iterator(); + + while (iter.hasNext()) { + final 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(final Object key, final 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(final 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(final Object key) { + final 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(final GraphNode v, final 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(final GraphNode v, final 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 = ""; + + final Iterator iter = nodes.values().iterator(); + + while (iter.hasNext()) { + final 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(final 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(final GraphNode v, final 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 { + HashMap map = new HashMap(); + + void removeNodeFromMap(final Object key) { + map.remove(key); + } + + void putNodeInMap(final Object key, final Object value) { + map.put(key, value); + } + + public Object remove(final Object key) { + final GraphNode v = (GraphNode) map.get(key); + + if (v != null) { + Graph.this.removeNode(v); + } + + return v; + } + + public Object put(final Object key, final Object value) { + final GraphNode v = (GraphNode) remove(key); + Graph.this.addNode(key, (GraphNode) value); + return v; + } + + public void clear() { + final Iterator iter = entrySet().iterator(); + + while (iter.hasNext()) { + final 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(final Object a) { + return entries.contains(a); + } + + public boolean remove(final Object a) { + final Map.Entry e = (Map.Entry) a; + + removingNode++; + Graph.this.removeNode(e.getKey()); + removingNode--; + + return entries.remove(a); + } + + public void clear() { + final Iterator iter = entries.iterator(); + + while (iter.hasNext()) { + final 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 { + int edgeModCount; + + NodeList() { + super(Graph.this.size()); + edgeModCount = Graph.this.edgeModCount; + } + + boolean addNode(final GraphNode a) { + return super.add(a); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean add(final Object a) { + throw new UnsupportedOperationException(); + } + + public boolean remove(final Object a) { + throw new UnsupportedOperationException(); + } + + // This works only if each node is in the list at most once. + public int indexOf(final Object a) { + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + final 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(final Object a, final int index) { + final 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(final Object a) { + if (edgeModCount != Graph.this.edgeModCount) { + throw new ConcurrentModificationException(); + } + + final 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(final Object a, final int index) { + final 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(final GraphNode node, final 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(final 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(final Collection c) { + return super.removeAll(new ArrayList(c)); + } + + /** + * Adds all of the nodes in a Collection to this set. + */ + public boolean addAll(final Collection c) { + return super.addAll(new ArrayList(c)); + } + + public boolean add(final Object a) { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + Assert.isTrue(nodes.containsValue(a)); + Assert.isTrue(nodes.containsValue(node)); + + final 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(final Object a) { + if (nodeModCount != Graph.this.nodeModCount) { + throw new ConcurrentModificationException(); + } + + final 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(final 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(); + } + + final Iterator iter = set.iterator(); + + while (iter.hasNext()) { + final 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..1a7ebe7 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/GraphNode.java @@ -0,0 +1,89 @@ +/* + * 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. + * + * 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 + */ + +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 HashSet succs; + + protected HashSet preds; + + protected int preIndex; + + protected int postIndex; + + /** + * Constructor. + */ + public GraphNode() { + succs = new HashSet(); + preds = new HashSet(); + 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(final int index) { + preIndex = index; + } + + void setPostOrderIndex(final 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..da1bcc6 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/IdentityComparator.java @@ -0,0 +1,43 @@ +/* + * 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. + * + * 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 + */ + +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(final Object o1, final Object o2) { + final int n1 = System.identityHashCode(o1); + final 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..e6a9860 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/ImmutableIterator.java @@ -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. + * + * 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 + */ + +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(final 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..4556785 --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/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. +# +# 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 + + +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..29333db --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/ResizeableArrayList.java @@ -0,0 +1,61 @@ +/* + * 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. + * + * 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 + */ + +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 { + /** + * This constructor is no longer supported in JDK1.2 public + * ResizeableArrayList(int initialCapacity, int capacityIncrement) { + * super(initialCapacity, capacityIncrement); } + */ + public ResizeableArrayList(final int initialCapacity) { + super(initialCapacity); + } + + public ResizeableArrayList() { + super(); + } + + public ResizeableArrayList(final Collection c) { + super(c); + } + + public void ensureSize(final int size) { + ensureCapacity(size); + + while (size() < size) { + add(null); + } + } + + public Object clone() { + return 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..3e5f47d --- /dev/null +++ b/src/EDU/purdue/cs/bloat/util/UnionFind.java @@ -0,0 +1,178 @@ +/* + * 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. + * + * 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 + */ + +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(final 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(final int a) { + nodes.ensureSize(a + 1); + + final Node na = (Node) nodes.get(a); + + if (na == null) { + // Start a new set with a + final 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(final int a) { + return findNode(a).value; + } + + /** + * Finds the set containing a given Node. + */ + private Node findNode(Node node) { + final 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. + final 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(final int a, final int b) { + return findNode(a) == findNode(b); + } + + /** + * Combines the set that contains a with the set that contains b. + */ + public void union(final int a, final int b) { + final Node na = findNode(a); + final 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(final 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